.net 使用 HTTPWebrequest (multipart/form-data) 上传文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/566462/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 12:10:26  来源:igfitidea点击:

Upload files with HTTPWebrequest (multipart/form-data)

.netuploadhttpwebrequest

提问by dr. evil

Is there any class, library or some piece of code which will help me to upload files with HTTPWebrequest?

是否有任何类、库或一些代码可以帮助我使用HTTPWebrequest上传文件?

Edit 2:

编辑2:

I do not want to upload to a WebDAV folder or something like that. I want to simulate a browser, so just like you upload your avatar to a forum or upload a file via form in a web application. Upload to a form which uses a multipart/form-data.

我不想上传到 WebDAV 文件夹或类似的东西。我想模拟一个浏览器,就像您将头像上传到论坛或通过 Web 应用程序中的表单上传文件一样。上传到使用 multipart/form-data 的表单。

Edit:

编辑:

WebClient is not cover my requirements, so I'm looking for a solution with HTTPWebrequest.

WebClient 没有满足我的要求,所以我正在寻找一个带有HTTPWebrequest的解决方案。

采纳答案by dr. evil

I was looking for something like this, Found in : http://bytes.com/groups/net-c/268661-how-upload-file-via-c-code(modified for correctness):

我正在寻找这样的东西,发现于:http: //bytes.com/groups/net-c/268661-how-upload-file-via-c-code(为了正确性而修改):

public static string UploadFilesToRemoteUrl(string url, string[] files, NameValueCollection formFields = null)
{
    string boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");

    HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
    request.ContentType = "multipart/form-data; boundary=" +
                            boundary;
    request.Method = "POST";
    request.KeepAlive = true;

    Stream memStream = new System.IO.MemoryStream();

    var boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" +
                                                            boundary + "\r\n");
    var endBoundaryBytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" +
                                                                boundary + "--");


    string formdataTemplate = "\r\n--" + boundary +
                                "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}";

    if (formFields != null)
    {
        foreach (string key in formFields.Keys)
        {
            string formitem = string.Format(formdataTemplate, key, formFields[key]);
            byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
            memStream.Write(formitembytes, 0, formitembytes.Length);
        }
    }

    string headerTemplate =
        "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" +
        "Content-Type: application/octet-stream\r\n\r\n";

    for (int i = 0; i < files.Length; i++)
    {
        memStream.Write(boundarybytes, 0, boundarybytes.Length);
        var header = string.Format(headerTemplate, "uplTheFile", files[i]);
        var headerbytes = System.Text.Encoding.UTF8.GetBytes(header);

        memStream.Write(headerbytes, 0, headerbytes.Length);

        using (var fileStream = new FileStream(files[i], FileMode.Open, FileAccess.Read))
        {
            var buffer = new byte[1024];
            var bytesRead = 0;
            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
            {
                memStream.Write(buffer, 0, bytesRead);
            }
        }
    }

    memStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length);
    request.ContentLength = memStream.Length;

    using (Stream requestStream = request.GetRequestStream())
    {
        memStream.Position = 0;
        byte[] tempBuffer = new byte[memStream.Length];
        memStream.Read(tempBuffer, 0, tempBuffer.Length);
        memStream.Close();
        requestStream.Write(tempBuffer, 0, tempBuffer.Length);
    }

    using (var response = request.GetResponse())
    {
        Stream stream2 = response.GetResponseStream();
        StreamReader reader2 = new StreamReader(stream2);
        return reader2.ReadToEnd();
    }
}

回答by Cristian Romanescu

Took the code above and fixed because it throws Internal Server Error 500. There are some problems with \r\n badly positioned and spaces etc. Applied the refactoring with memory stream, writing directly to the request stream. Here is the result:

使用上面的代码并修复,因为它会引发内部服务器错误 500。存在一些问题,如 \r\n 位置错误和空格等。应用内存流重构,直接写入请求流。结果如下:

    public static void HttpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc) {
        log.Debug(string.Format("Uploading {0} to {1}", file, url));
        string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
        byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

        HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(url);
        wr.ContentType = "multipart/form-data; boundary=" + boundary;
        wr.Method = "POST";
        wr.KeepAlive = true;
        wr.Credentials = System.Net.CredentialCache.DefaultCredentials;

        Stream rs = wr.GetRequestStream();

        string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
        foreach (string key in nvc.Keys)
        {
            rs.Write(boundarybytes, 0, boundarybytes.Length);
            string formitem = string.Format(formdataTemplate, key, nvc[key]);
            byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
            rs.Write(formitembytes, 0, formitembytes.Length);
        }
        rs.Write(boundarybytes, 0, boundarybytes.Length);

        string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
        string header = string.Format(headerTemplate, paramName, file, contentType);
        byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
        rs.Write(headerbytes, 0, headerbytes.Length);

        FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);
        byte[] buffer = new byte[4096];
        int bytesRead = 0;
        while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) {
            rs.Write(buffer, 0, bytesRead);
        }
        fileStream.Close();

        byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
        rs.Write(trailer, 0, trailer.Length);
        rs.Close();

        WebResponse wresp = null;
        try {
            wresp = wr.GetResponse();
            Stream stream2 = wresp.GetResponseStream();
            StreamReader reader2 = new StreamReader(stream2);
            log.Debug(string.Format("File uploaded, server response is: {0}", reader2.ReadToEnd()));
        } catch(Exception ex) {
            log.Error("Error uploading file", ex);
            if(wresp != null) {
                wresp.Close();
                wresp = null;
            }
        } finally {
            wr = null;
        }
    }

and sample usage:

和示例用法:

    NameValueCollection nvc = new NameValueCollection();
    nvc.Add("id", "TTR");
    nvc.Add("btn-submit-photo", "Upload");
    HttpUploadFile("http://your.server.com/upload", 
         @"C:\test\test.jpg", "file", "image/jpeg", nvc);

It could be extended to handle multiple files or just call it multiple times for each file. However it suits your needs.

它可以扩展为处理多个文件,或者只是为每个文件多次调用它。但是,它适合您的需求。

回答by Joshcodes

UPDATE: Using .NET 4.5 (or .NET 4.0 by adding the Microsoft.Net.Httppackage from NuGet) this is possible without external code, extensions, and "low level" HTTP manipulation. Here is an example:

更新:使用 .NET 4.5(或通过添加来自 NuGet的Microsoft.Net.Http包来使用 .NET 4.0 )这在没有外部代码、扩展和“低级”HTTP 操作的情况下是可能的。下面是一个例子:

// Perform the equivalent of posting a form with a filename and two files, in HTML:
// <form action="{url}" method="post" enctype="multipart/form-data">
//     <input type="text" name="filename" />
//     <input type="file" name="file1" />
//     <input type="file" name="file2" />
// </form>
private async Task<System.IO.Stream> UploadAsync(string url, string filename, Stream fileStream, byte [] fileBytes)
{
    // Convert each of the three inputs into HttpContent objects

    HttpContent stringContent = new StringContent(filename);
    // examples of converting both Stream and byte [] to HttpContent objects
    // representing input type file
    HttpContent fileStreamContent = new StreamContent(fileStream);
    HttpContent bytesContent = new ByteArrayContent(fileBytes);

    // Submit the form using HttpClient and 
    // create form data as Multipart (enctype="multipart/form-data")

    using (var client = new HttpClient())
    using (var formData = new MultipartFormDataContent()) 
    {
        // Add the HttpContent objects to the form data

        // <input type="text" name="filename" />
        formData.Add(stringContent, "filename", "filename");
        // <input type="file" name="file1" />
        formData.Add(fileStreamContent, "file1", "file1");
        // <input type="file" name="file2" />
        formData.Add(bytesContent, "file2", "file2");

        // Invoke the request to the server

        // equivalent to pressing the submit button on
        // a form with attributes (action="{url}" method="post")
        var response = await client.PostAsync(url, formData);

        // ensure the request was a success
        if (!response.IsSuccessStatusCode)
        {
            return null;
        }
        return await response.Content.ReadAsStreamAsync();
    }
}

回答by Chris Hynes

My ASP.NET Upload FAQ has an article on this, with example code: Upload files using an RFC 1867 POST request with HttpWebRequest/WebClient. This code doesn't load files into memory (as opposed to the code above), supports multiple files, and supports form values, setting credentials and cookies, etc.

我的 ASP.NET 上传常见问题有一篇关于此的文章,其中包含示例代码:Upload files using an RFC 1867 POST request with HttpWebRequest/WebClient。此代码不会将文件加载到内存中(与上面的代码相反),支持多个文件,并支持表单值、设置凭据和 cookie 等。

Edit: looks like Axosoft took down the page. Thanks guys.

编辑:看起来 Axosoft 删除了该页面。谢谢你们。

It's still accessible via archive.org.

它仍然可以通过 archive.org 访问。

回答by Stefan

Based on the code provided above I added support for multiple files and also uploading a stream directly without the need to have a local file.

基于上面提供的代码,我添加了对多个文件的支持,还可以直接上传流而无需本地文件。

To upload files to a specific url including some post params do the following:

要将文件上传到包含一些帖子参数的特定网址,请执行以下操作:

RequestHelper.PostMultipart(
    "http://www.myserver.com/upload.php", 
    new Dictionary<string, object>() {
        { "testparam", "my value" },
        { "file", new FormFile() { Name = "image.jpg", ContentType = "image/jpeg", FilePath = "c:\temp\myniceimage.jpg" } },
        { "other_file", new FormFile() { Name = "image2.jpg", ContentType = "image/jpeg", Stream = imageDataStream } },
    });

To enhance this even more one could determine the name and mime type from the given file itself.

为了进一步增强这一点,可以从给定文件本身确定名称和 MIME 类型。

public class FormFile 
{
    public string Name { get; set; }

    public string ContentType { get; set; }

    public string FilePath { get; set; }

    public Stream Stream { get; set; }
}

public class RequestHelper
{

    public static string PostMultipart(string url, Dictionary<string, object> parameters) {

        string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
        byte[] boundaryBytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.ContentType = "multipart/form-data; boundary=" + boundary;
        request.Method = "POST";
        request.KeepAlive = true;
        request.Credentials = System.Net.CredentialCache.DefaultCredentials;

        if(parameters != null && parameters.Count > 0) {

            using(Stream requestStream = request.GetRequestStream()) {

                foreach(KeyValuePair<string, object> pair in parameters) {

                    requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                    if(pair.Value is FormFile) {
                        FormFile file = pair.Value as FormFile;
                        string header = "Content-Disposition: form-data; name=\"" + pair.Key + "\"; filename=\"" + file.Name + "\"\r\nContent-Type: " + file.ContentType + "\r\n\r\n";
                        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(header);
                        requestStream.Write(bytes, 0, bytes.Length);
                        byte[] buffer = new byte[32768];
                        int bytesRead;
                        if(file.Stream == null) {
                            // upload from file
                            using(FileStream fileStream = File.OpenRead(file.FilePath)) {
                                while((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                                    requestStream.Write(buffer, 0, bytesRead);
                                fileStream.Close();
                            }
                        }
                        else {
                            // upload from given stream
                            while((bytesRead = file.Stream.Read(buffer, 0, buffer.Length)) != 0)
                                requestStream.Write(buffer, 0, bytesRead);
                        }
                    }
                    else {
                        string data = "Content-Disposition: form-data; name=\"" + pair.Key + "\"\r\n\r\n" + pair.Value;
                        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
                        requestStream.Write(bytes, 0, bytes.Length);
                    }
                }

                byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
                requestStream.Write(trailer, 0, trailer.Length);
                requestStream.Close();
            }
        }

        using(WebResponse response = request.GetResponse()) {
            using(Stream responseStream = response.GetResponseStream())
            using(StreamReader reader = new StreamReader(responseStream))
                return reader.ReadToEnd();
        }


    }
}

回答by Moose

something like this is close: (untested code)

这样的事情很接近:(未经测试的代码)

byte[] data; // data goes here.

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Credentials = userNetworkCredentials;
request.Method = "PUT";
request.ContentType = "application/octet-stream";
request.ContentLength = data.Length;
Stream stream = request.GetRequestStream();
stream.Write(data,0,data.Length);
stream.Close();
response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
temp = reader.ReadToEnd();
reader.Close();

回答by John T

I think you're looking for something more like WebClient.

我认为您正在寻找更像WebClient 的东西。

Specifically, UploadFile().

具体来说,UploadFile()

回答by mopenstein

Took the above and modified it accept some header values, and multiple files

采取上述并修改它接受一些标题值和多个文件

    NameValueCollection headers = new NameValueCollection();
        headers.Add("Cookie", "name=value;");
        headers.Add("Referer", "http://google.com");
    NameValueCollection nvc = new NameValueCollection();
        nvc.Add("name", "value");

    HttpUploadFile(url, new string[] { "c:\file1.txt", "c:\file2.jpg" }, new string[] { "file", "image" }, new string[] { "application/octet-stream", "image/jpeg" }, nvc, headers);


public static void HttpUploadFile(string url, string[] file, string[] paramName, string[] contentType, NameValueCollection nvc, NameValueCollection headerItems)
{
    //log.Debug(string.Format("Uploading {0} to {1}", file, url));
    string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
    byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

    HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(url);

    foreach (string key in headerItems.Keys)
    {
        if (key == "Referer")
        {
            wr.Referer = headerItems[key];
        }
        else
        {
            wr.Headers.Add(key, headerItems[key]);
        }
    }

    wr.ContentType = "multipart/form-data; boundary=" + boundary;
    wr.Method = "POST";
    wr.KeepAlive = true;
    wr.Credentials = System.Net.CredentialCache.DefaultCredentials;

    Stream rs = wr.GetRequestStream();

    string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
    foreach (string key in nvc.Keys)
    {
        rs.Write(boundarybytes, 0, boundarybytes.Length);
        string formitem = string.Format(formdataTemplate, key, nvc[key]);
        byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
        rs.Write(formitembytes, 0, formitembytes.Length);
    }
    rs.Write(boundarybytes, 0, boundarybytes.Length);

    string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
    string header = "";

    for(int i =0; i<file.Count();i++)
    {
        header = string.Format(headerTemplate, paramName[i], System.IO.Path.GetFileName(file[i]), contentType[i]);
        byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
        rs.Write(headerbytes, 0, headerbytes.Length);

        FileStream fileStream = new FileStream(file[i], FileMode.Open, FileAccess.Read);
        byte[] buffer = new byte[4096];
        int bytesRead = 0;
        while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
        {
            rs.Write(buffer, 0, bytesRead);
        }
        fileStream.Close();
        rs.Write(boundarybytes, 0, boundarybytes.Length);
    }
    rs.Close();

    WebResponse wresp = null;
    try
    {
        wresp = wr.GetResponse();
        Stream stream2 = wresp.GetResponseStream();
        StreamReader reader2 = new StreamReader(stream2);
        //log.Debug(string.Format("File uploaded, server response is: {0}", reader2.ReadToEnd()));
    }
    catch (Exception ex)
    {
        //log.Error("Error uploading file", ex);
            wresp.Close();
            wresp = null;
    }
    finally
    {
        wr = null;
    }
}

回答by Keith Walton

VB Example (converted from C# example on another post):

VB 示例(从另一篇文章中的 C# 示例转换而来):

Private Sub HttpUploadFile( _
    ByVal uri As String, _
    ByVal filePath As String, _
    ByVal fileParameterName As String, _
    ByVal contentType As String, _
    ByVal otherParameters As Specialized.NameValueCollection)

    Dim boundary As String = "---------------------------" & DateTime.Now.Ticks.ToString("x")
    Dim newLine As String = System.Environment.NewLine
    Dim boundaryBytes As Byte() = Text.Encoding.ASCII.GetBytes(newLine & "--" & boundary & newLine)
    Dim request As Net.HttpWebRequest = Net.WebRequest.Create(uri)

    request.ContentType = "multipart/form-data; boundary=" & boundary
    request.Method = "POST"
    request.KeepAlive = True
    request.Credentials = Net.CredentialCache.DefaultCredentials

    Using requestStream As IO.Stream = request.GetRequestStream()

        Dim formDataTemplate As String = "Content-Disposition: form-data; name=""{0}""{1}{1}{2}"

        For Each key As String In otherParameters.Keys

            requestStream.Write(boundaryBytes, 0, boundaryBytes.Length)
            Dim formItem As String = String.Format(formDataTemplate, key, newLine, otherParameters(key))
            Dim formItemBytes As Byte() = Text.Encoding.UTF8.GetBytes(formItem)
            requestStream.Write(formItemBytes, 0, formItemBytes.Length)

        Next key

        requestStream.Write(boundaryBytes, 0, boundaryBytes.Length)

        Dim headerTemplate As String = "Content-Disposition: form-data; name=""{0}""; filename=""{1}""{2}Content-Type: {3}{2}{2}"
        Dim header As String = String.Format(headerTemplate, fileParameterName, filePath, newLine, contentType)
        Dim headerBytes As Byte() = Text.Encoding.UTF8.GetBytes(header)
        requestStream.Write(headerBytes, 0, headerBytes.Length)

        Using fileStream As New IO.FileStream(filePath, IO.FileMode.Open, IO.FileAccess.Read)

            Dim buffer(4096) As Byte
            Dim bytesRead As Int32 = fileStream.Read(buffer, 0, buffer.Length)

            Do While (bytesRead > 0)

                requestStream.Write(buffer, 0, bytesRead)
                bytesRead = fileStream.Read(buffer, 0, buffer.Length)

            Loop

        End Using

        Dim trailer As Byte() = Text.Encoding.ASCII.GetBytes(newLine & "--" + boundary + "--" & newLine)
        requestStream.Write(trailer, 0, trailer.Length)

    End Using

    Dim response As Net.WebResponse = Nothing

    Try

        response = request.GetResponse()

        Using responseStream As IO.Stream = response.GetResponseStream()

            Using responseReader As New IO.StreamReader(responseStream)

                Dim responseText = responseReader.ReadToEnd()
                Diagnostics.Debug.Write(responseText)

            End Using

        End Using

    Catch exception As Net.WebException

        response = exception.Response

        If (response IsNot Nothing) Then

            Using reader As New IO.StreamReader(response.GetResponseStream())

                Dim responseText = reader.ReadToEnd()
                Diagnostics.Debug.Write(responseText)

            End Using

            response.Close()

        End If

    Finally

        request = Nothing

    End Try

End Sub

回答by Chris

I had to deal with this recently - another way to approach it is to use the fact that WebClient is inheritable, and change the underlying WebRequest from there:

我最近不得不处理这个问题——另一种方法是利用 WebClient 是可继承的这一事实,并从那里更改底层 WebRequest:

http://msdn.microsoft.com/en-us/library/system.net.webclient.getwebrequest(VS.80).aspx

http://msdn.microsoft.com/en-us/library/system.net.webclient.getwebrequest(VS.80).aspx

I prefer C#, but if you're stuck with VB the results will look something like this:

我更喜欢 C#,但如果你坚持使用 VB,结果将如下所示:

Public Class BigWebClient
    Inherits WebClient
    Protected Overrides Function GetWebRequest(ByVal address As System.Uri) As System.Net.WebRequest
        Dim x As WebRequest = MyBase.GetWebRequest(address)
        x.Timeout = 60 * 60 * 1000
        Return x
    End Function
End Class

'Use BigWebClient here instead of WebClient