使用.NET发布多个文件和表单值(控制台应用程序)

时间:2012-02-04 17:23:40

标签: c# .net http file-upload system.net

我想将多个文件和表单变量POST到CGI脚本,所有这些都在一个 HTTP请求中。我相信这需要一个multipart/form-data编码的HTTP帖子。这是一个发送所需信息的示例HTML表单;我需要通过应用程序发送相同的信息:

<form action="/process.php" enctype="multipart/form-data" method="post">
<input type="text" name="foo" value="bar">
<input type="text" name="blah" value="baz">
<input type="file" name="file1">
<input type="file" name="file2">
<input type="file" name="file3">
</form>

请注意,该应用程序是C#.NET GUI(或控制台)应用程序,而不是ASP.Net应用程序。

2 个答案:

答案 0 :(得分:20)

我去年根据我发现的一些代码做了这个。它完全支持您想要的文件和值。

以下是名为HttpForm的课程:

public class HttpForm {

    private Dictionary<string, string> _files = new Dictionary<string, string>();
    private Dictionary<string, string> _values = new Dictionary<string, string>();

    public HttpForm(string url) {
        this.Url = url;
        this.Method = "POST";
    }

    public string Method { get; set; }
    public string Url { get; set; }

    //return self so that we can chain
    public HttpForm AttachFile(string field, string fileName) {
        _files[field] = fileName;
        return this;
    }

    public HttpForm ResetForm(){
        _files.Clear();
        _values.Clear();
        return this;
    }

    //return self so that we can chain
    public HttpForm SetValue(string field, string value) {
        _values[field] = value;
        return this;
    }

    public HttpWebResponse Submit() {
        return this.UploadFiles(_files, _values);
    }


    private HttpWebResponse UploadFiles(Dictionary<string, string> files, Dictionary<string, string> otherValues) {
        var req = (HttpWebRequest)WebRequest.Create(this.Url);

        req.Timeout = 10000 * 1000;
        req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
        req.AllowAutoRedirect = false;

        var mimeParts = new List<MimePart>();
        try {
            if (otherValues != null) {
                foreach (var fieldName in otherValues.Keys) {
                    var part = new MimePart();

                    part.Headers["Content-Disposition"] = "form-data; name=\"" + fieldName + "\"";
                    part.Data = new MemoryStream(Encoding.UTF8.GetBytes(otherValues[fieldName]));

                    mimeParts.Add(part);
                }
            }

            if (files != null) {
                foreach (var fieldName in files.Keys) {
                    var part = new MimePart();

                    part.Headers["Content-Disposition"] = "form-data; name=\"" + fieldName + "\"; filename=\"" + files[fieldName] + "\"";
                    part.Headers["Content-Type"] = "application/octet-stream";
                    part.Data = File.OpenRead(files[fieldName]);

                    mimeParts.Add(part);
                }
            }

            string boundary = "----------" + DateTime.Now.Ticks.ToString("x");

            req.ContentType = "multipart/form-data; boundary=" + boundary;
            req.Method = this.Method;

            long contentLength = 0;

            byte[] _footer = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n");

            foreach (MimePart part in mimeParts) {
                contentLength += part.GenerateHeaderFooterData(boundary);
            }

            req.ContentLength = contentLength + _footer.Length;

            byte[] buffer = new byte[8192];
            byte[] afterFile = Encoding.UTF8.GetBytes("\r\n");
            int read;

            using (Stream s = req.GetRequestStream()) {
                foreach (MimePart part in mimeParts) {
                    s.Write(part.Header, 0, part.Header.Length);

                    while ((read = part.Data.Read(buffer, 0, buffer.Length)) > 0)
                        s.Write(buffer, 0, read);

                    part.Data.Dispose();

                    s.Write(afterFile, 0, afterFile.Length);
                }

                s.Write(_footer, 0, _footer.Length);
            }

            var res = (HttpWebResponse)req.GetResponse();

            return res;
        } catch (Exception ex) {
            Console.WriteLine(ex.Message);
            foreach (MimePart part in mimeParts)
                if (part.Data != null)
                    part.Data.Dispose();

            return (HttpWebResponse)req.GetResponse();
        }
    }

    private class MimePart {
        private NameValueCollection _headers = new NameValueCollection();
        public NameValueCollection Headers { get { return _headers; } }

        public byte[] Header { get; protected set; }

        public long GenerateHeaderFooterData(string boundary) {
            StringBuilder sb = new StringBuilder();

            sb.Append("--");
            sb.Append(boundary);
            sb.AppendLine();
            foreach (string key in _headers.AllKeys) {
                sb.Append(key);
                sb.Append(": ");
                sb.AppendLine(_headers[key]);
            }
            sb.AppendLine();

            Header = Encoding.UTF8.GetBytes(sb.ToString());

            return Header.Length + Data.Length + 2;
        }

        public Stream Data { get; set; }
    }
}

你可以像这样使用它:

var file1 = @"C:\file";
var file2 = @"C:\file2";

var yourUrl = "http://yourdomain.com/process.php";
var httpForm = new HttpForm(yourUrl);
httpForm.AttachFile("file1", file1).AttachFile("file2", file2);
httpForm.setValue("foo", "some foo").setValue("blah", "rarrr!");
httpForm.Submit();

让我知道它是否适合你。

答案 1 :(得分:1)

我去年必须在一个项目上做同样的事情。经过一番环顾,我发现了这个:

Upload files with HTTPWebrequest (multipart/form-data)

那里的第二个答案应该是你正在寻找的东西。当我试图这样做时,我遇到了一些麻烦,让这个确切的方法工作。问题是C#.NET对POST中的多个键/值对没有任何支持。因此,您必须自己构建HTTP请求内容标头。我相信上面链接中的答案将其直接写入请求流。我能够转换下面链接中找到的代码来构建标头,然后将字节写入请求流。

http://code.activestate.com/recipes/146306-http-client-to-post-using-multipartform-data/

我的代码看起来类似于以下内容(为了便于阅读,我必须在下面对其进行修改,因为很多内容被抽象为整个项目的一部分,所以它在语法上可能不完美)。

    public void BuildHeader(string contentType, string filename)
    {

        if ((contentType == null) ||
            (contentType.Length == 0))
        {
            contentType = "application/octet-stream";
        }

        // Create the boundary string for the POST message header
        string boundary = "----------" + DateTime.Now.Ticks.ToString("x");

        // Build up the POST message header
        StringBuilder sb = new StringBuilder();

        // The specific format used can be found in the HTTP protocol specs.
        // The 'name' variable indicates the field-name, and the last variable
        // added to the string before another boundary is the value for that field.
        sb.Append("--");
        sb.Append(boundary);
        sb.Append("\r\n");
        sb.Append("Content-Disposition: form-data; name=\"");
        sb.Append("path");
        sb.Append("\"");
        sb.Append("\r\n\r\n");
        sb.Append(fileName);

        sb.Append("--");
        sb.Append(boundary);
        sb.Append("\r\n");
        sb.Append("Content-Disposition: form-data; name=\"");
        sb.Append("contents");
        sb.Append("\"; fileName=\"");
        sb.Append("abc");
        sb.Append("\"");
        sb.Append("\r\n");
        sb.Append("Content-Type: ");
        sb.Append(contentType);
        sb.Append("\r\n");
        sb.Append("\r\n");

        using (Stream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
        {
            // Add the file contents to the POST message
            byte[] buffer = new Byte[checked((uint)Math.Min(4096, (int)fileStream.Length))];
            int bytesRead = 0;
            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
            {
                sb.Append(ASCIIEncoding.ASCII.GetString(buffer));
            }

            // Get the byte array of the POST message, and its length
            string totalContents = sb.ToString();
            byte[] totalUpload = Encoding.UTF8.GetBytes(totalContents);
            int length = totalUpload.Length;
        }
    }

请注意,这只会为上传单个文件准备一个标题。