Vielen Dank für die Antworten, liebe Leute! Ich musste dies vor kurzem zum Laufen bringen und habe Ihre Vorschläge intensiv genutzt. Allerdings gab es ein paar knifflige Stellen, die nicht wie erwartet funktionierten, vor allem was das Einbinden der Datei betrifft (was ein wichtiger Teil der Frage war). Es gibt hier bereits viele Antworten, aber ich denke, dass dies für jemanden in der Zukunft nützlich sein könnte (ich konnte online nicht viele klare Beispiele dafür finden). I schrieb einen Blogbeitrag das erklärt es ein wenig mehr.
Ich habe zunächst versucht, die Dateidaten als UTF8-kodierte Zeichenfolge zu übergeben, aber ich hatte Probleme mit der Kodierung von Dateien (es funktionierte gut für eine einfache Textdatei, aber beim Hochladen eines Word-Dokuments, zum Beispiel, wenn ich versuchte, die Datei zu speichern, die mit Request.Files[0].SaveAs() an das Formular übergeben wurde, funktionierte das Öffnen der Datei in Word nicht richtig. Ich habe herausgefunden, dass es wie erwartet funktioniert, wenn man die Dateidaten direkt mit einem Stream (und nicht mit einem StringBuilder) schreibt. Außerdem habe ich ein paar Änderungen vorgenommen, die mir das Verständnis erleichtern.
Übrigens, die Mehrteilige Formulare Aufforderung zur Stellungnahme und die W3C-Empfehlung für mulitpart/form-data sind einige nützliche Quellen, falls jemand eine Referenz für die Spezifikation benötigt.
Ich habe die Klasse WebHelpers geändert, damit sie etwas kleiner ist und einfachere Schnittstellen hat. Sie heißt jetzt FormUpload
. Wenn Sie eine FormUpload.FileParameter
können Sie den Byte[]-Inhalt zusammen mit einem Dateinamen und einem Inhaltstyp übergeben, und wenn Sie eine Zeichenkette übergeben, wird diese als Standard-Namen/Wert-Kombination behandelt.
Hier ist die FormUpload-Klasse:
// Implements multipart/form-data POST in 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<string, object> 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");
}
// Set up the request properties.
request.Method = "POST";
request.ContentType = contentType;
request.UserAgent = userAgent;
request.CookieContainer = new CookieContainer();
request.ContentLength = formData.Length;
// You could add authentication here as well if needed:
// request.PreAuthenticate = true;
// request.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
// request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(System.Text.Encoding.Default.GetBytes("username" + ":" + "password")));
// Send the form data to the request.
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(formData, 0, formData.Length);
requestStream.Close();
}
return request.GetResponse() as HttpWebResponse;
}
private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
{
Stream formDataStream = new System.IO.MemoryStream();
bool needsCLRF = false;
foreach (var param in postParameters)
{
// Thanks to feedback from commenters, add a CRLF to allow multiple parameters to be added.
// Skip it on the first parameter, add it to subsequent parameters.
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;
// Add just the first part of this param, since we will write the file data directly to the 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));
// Write the file data directly to the Stream, rather than serializing it to a string.
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));
}
}
// Add the end of the request. Start with a newline
string footer = "\r\n--" + boundary + "--\r\n";
formDataStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer));
// Dump the Stream into a 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;
}
}
}
Hier ist der aufrufende Code, der eine Datei und ein paar normale Postparameter hochlädt:
// Read file data
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();
// Generate post objects
Dictionary<string, object> postParameters = new Dictionary<string, object>();
postParameters.Add("filename", "People.doc");
postParameters.Add("fileformat", "doc");
postParameters.Add("file", new FormUpload.FileParameter(data, "People.doc", "application/msword"));
// Create request and receive response
string postURL = "http://localhost";
string userAgent = "Someone";
HttpWebResponse webResponse = FormUpload.MultipartFormDataPost(postURL, userAgent, postParameters);
// Process response
StreamReader responseReader = new StreamReader(webResponse.GetResponseStream());
string fullResponse = responseReader.ReadToEnd();
webResponse.Close();
Response.Write(fullResponse);