Merci pour les réponses, tout le monde! J'ai récemment dû faire fonctionner cela, et j'ai beaucoup utilisé vos suggestions. Cependant, il y a eu quelques parties délicates qui n'ont pas fonctionné comme prévu, principalement en ce qui concerne l'inclusion du fichier (ce qui était une partie importante de la question). Il y a déjà beaucoup de réponses ici, mais je pense que cela pourrait être utile à quelqu'un à l'avenir (je n'ai pas trouvé beaucoup d'exemples clairs en ligne). J'ai écrit un article de blog qui l'explique un peu plus.
En gros, j'ai d'abord essayé de passer les données du fichier en tant que chaîne encodée en UTF8, mais j'avais des problèmes avec l'encodage des fichiers (cela fonctionnait bien pour un fichier texte brut, mais lors de l'envoi d'un document Word, par exemple, si j'essayais d'enregistrer le fichier qui était transmis dans le formulaire envoyé utilisant Request.Files[0].SaveAs(), l'ouverture du fichier dans Word ne fonctionnait pas correctement. J'ai trouvé que si vous écrivez les données du fichier directement en utilisant un Stream (plutôt qu'un StringBuilder), cela fonctionnait comme prévu. De plus, j'ai apporté quelques modifications qui m'ont facilité la compréhension.
Par ailleurs, le Request for Comments sur les formulaires multipartes et la Recommandation de la W3C pour multipart/form-data sont des ressources utiles au cas où quelqu'un aurait besoin d'une référence pour la spécification.
J'ai modifié la classe WebHelpers pour la rendre un peu plus petite et avoir des interfaces plus simples, elle s'appelle maintenant FormUpload
. Si vous passez un FormUpload.FileParameter
, vous pouvez transmettre les octets[] contenu avec un nom de fichier et un type de contenu, et si vous passez une chaîne, elle le traitera comme une combinaison de nom/valeur standard.
Voici la classe FormUpload :
// Implémente une méthode POST multipart/form-data en C# http://www.ietf.org/rfc/rfc2388.txt
// http://www.briangrinstead.com/blog/multipart-form-post-in-c
public static class FormUpload
{
private static readonly Encoding encoding = Encoding.UTF8;
public static HttpWebResponse MultipartFormDataPost(string postUrl, string userAgent, Dictionary postParameters)
{
string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid());
string contentType = "multipart/form-data; boundary=" + formDataBoundary;
byte[] formData = GetMultipartFormData(postParameters, formDataBoundary);
return PostForm(postUrl, userAgent, contentType, formData);
}
private static HttpWebResponse PostForm(string postUrl, string userAgent, string contentType, byte[] formData)
{
HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;
if (request == null)
{
throw new NullReferenceException("request is not a http request");
}
// Configure les propriétés de la requête.
request.Method = "POST";
request.ContentType = contentType;
request.UserAgent = userAgent;
request.CookieContainer = new CookieContainer();
request.ContentLength = formData.Length;
// Vous pouvez également ajouter une authentification ici si nécessaire :
// request.PreAuthenticate = true;
// request.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
// request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(System.Text.Encoding.Default.GetBytes("nom d'utilisateur" + ":" + "mot de passe")));
// Envoie les données du formulaire à la requête.
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(formData, 0, formData.Length);
requestStream.Close();
}
return request.GetResponse() as HttpWebResponse;
}
private static byte[] GetMultipartFormData(Dictionary postParameters, string boundary)
{
Stream formDataStream = new System.IO.MemoryStream();
bool needsCLRF = false;
foreach (var param in postParameters)
{
// Grâce aux retours des commentateurs, ajoutez un CRLF pour permettre l'ajout de plusieurs paramètres.
// Sautez-le sur le premier paramètre, ajoutez-le aux paramètres suivants.
if (needsCLRF)
formDataStream.Write(encoding.GetBytes("\r\n"), 0, encoding.GetByteCount("\r\n"));
needsCLRF = true;
if (param.Value is FileParameter)
{
FileParameter fileToUpload = (FileParameter)param.Value;
// Ajoutez juste la première partie de ce paramètre, car nous écrirons directement les données du fichier dans le Stream
string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\";\r\nContent-Type: {3}\r\n\r\n",
boundary,
param.Key,
fileToUpload.FileName ?? param.Key,
fileToUpload.ContentType ?? "application/octet-stream");
formDataStream.Write(encoding.GetBytes(header), 0, encoding.GetByteCount(header));
// Écrivez directement les données du fichier dans le Stream, plutôt que de les sérialiser dans une chaîne.
formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
}
else
{
string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}",
boundary,
param.Key,
param.Value);
formDataStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData));
}
}
// Ajoutez la fin de la requête. Commencez par une nouvelle ligne
string footer = "\r\n--" + boundary + "--\r\n";
formDataStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer));
// Videz le Stream dans un byte[]
formDataStream.Position = 0;
byte[] formData = new byte[formDataStream.Length];
formDataStream.Read(formData, 0, formData.Length);
formDataStream.Close();
return formData;
}
public class FileParameter
{
public byte[] File { get; set; }
public string FileName { get; set; }
public string ContentType { get; set; }
public FileParameter(byte[] file) : this(file, null) { }
public FileParameter(byte[] file, string filename) : this(file, filename, null) { }
public FileParameter(byte[] file, string filename, string contenttype)
{
File = file;
FileName = filename;
ContentType = contenttype;
}
}
}
Voici le code d'appel, qui télécharge un fichier et quelques paramètres de post normaux :
// Lire les données du fichier
FileStream fs = new FileStream("c:\\people.doc", FileMode.Open, FileAccess.Read);
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);
fs.Close();
// Générer des objets de post
Dictionary postParameters = new Dictionary();
postParameters.Add("nomfichier", "People.doc");
postParameters.Add("formatfichier", "doc");
postParameters.Add("fichier", new FormUpload.FileParameter(data, "People.doc", "application/msword"));
// Créer et recevoir la requête de réponse
string postURL = "http://localhost";
string userAgent = "Quelqu'un";
HttpWebResponse webResponse = FormUpload.MultipartFormDataPost(postURL, userAgent, postParameters);
// Traiter la réponse
StreamReader responseReader = new StreamReader(webResponse.GetResponseStream());
string fullResponse = responseReader.ReadToEnd();
webResponse.Close();
Response.Write(fullResponse);
0 votes
Cela fonctionne comme un charme www.briangrinstead.com/blog
0 votes
Si vous n'êtes pas contre une petite dépendance de bibliothèque, Flurl rend cela aussi simple que possible. [Avis de non-responsabilité : Je suis l'auteur]