Отправка нескольких файлов и значений форм с помощью .NET (консольное приложение) - PullRequest
8 голосов
/ 04 февраля 2012

Я хочу отправить несколько файлов и сформировать переменные в сценарий CGI, все в один HTTP-запрос. Я считаю, что для этого требуется HTTP-сообщение с кодировкой multipart/form-data. Вот пример 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 ]

20 голосов
/ 04 февраля 2012

Я сделал это в прошлом году, основываясь на каком-то коде, который нашел.Он поддерживает именно то, что вы хотите, как файлы, так и значения.

Вот класс с именем 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 голос
/ 04 февраля 2012

Я должен был сделать то же самое в прошлом году. Посмотрев вокруг, я нашел это:

Загрузка файлов с помощью 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;
        }
    }

Обратите внимание, что это только готовит заголовок для загрузки одного файла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...