Многослойные формы от клиента C#

Если SomeType? SomeOptionalType не является обязательным и может быть null, то Guid SomeOptionalTypeId также может быть null.

Изменить Guid на Guid?:

[ForeignKey("SomeOptionalTypeId")]
public Guid? SomeOptionalTypeId { get; set; }
52
задан bash74 20 October 2008 в 09:38
поделиться

5 ответов

Thanks for the answers, everybody! I recently had to get this to work, and used your suggestions heavily. However, there were a couple of tricky parts that did not work as expected, mostly having to do with actually including the file (which was an important part of the question). There are a lot of answers here already, but I think this may be useful to someone in the future (I could not find many clear examples of this online). I wrote a blog post that explains it a little more.

Basically, I first tried to pass in the file data as a UTF8 encoded string, but I was having problems with encoding files (it worked fine for a plain text file, but when uploading a Word Document, for example, if I tried to save the file that was passed through to the posted form using Request.Files[0].SaveAs(), opening the file in Word did not work properly. I found that if you write the file data directly using a Stream (rather than a StringBuilder), it worked as expected. Also, I made a couple of modifications that made it easier for me to understand.

By the way, the Multipart Forms Request for Comments and the W3C Recommendation for mulitpart/form-data are a couple of useful resources in case anyone needs a reference for the specification.

I changed the WebHelpers class to be a bit smaller and have simpler interfaces, it is now called FormUpload. If you pass a FormUpload.FileParameter you can pass the byte[] contents along with a file name and content type, and if you pass a string, it will treat it as a standard name/value combination.

Here is the FormUpload class:

// 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;
        }
    }
}

Here is the calling code, which uploads a file and a few normal post parameters:

// 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);
70
ответ дан 7 November 2019 в 09:01
поделиться

Ниже приведен код, который я использую

    //This URL not exist, it's only an example.
    string url = "http://myBox.s3.amazonaws.com/";
    //Instantiate new CustomWebRequest class
    CustomWebRequest wr = new CustomWebRequest(url);
    //Set values for parameters
    wr.ParamsCollection.Add(new ParamsStruct("key", "${filename}"));
    wr.ParamsCollection.Add(new ParamsStruct("acl", "public-read"));
    wr.ParamsCollection.Add(new ParamsStruct("success_action_redirect", "http://www.yahoo.com"));
    wr.ParamsCollection.Add(new ParamsStruct("x-amz-meta-uuid", "14365123651274"));
    wr.ParamsCollection.Add(new ParamsStruct("x-amz-meta-tag", ""));
    wr.ParamsCollection.Add(new ParamsStruct("AWSAccessKeyId", "zzzz"));            
    wr.ParamsCollection.Add(new ParamsStruct("Policy", "adsfadsf"));
    wr.ParamsCollection.Add(new ParamsStruct("Signature", "hH6lK6cA="));
    //For file type, send the inputstream of selected file
    StreamReader sr = new StreamReader(@"file.txt");
    wr.ParamsCollection.Add(new ParamsStruct("file", sr, ParamsStruct.ParamType.File, "file.txt"));

    wr.PostData();

по следующей ссылке. Я загрузил тот же код. http://www.codeproject.com/KB/cs/multipart_request_C_.aspx

Любая справка

0
ответ дан 7 November 2019 в 09:01
поделиться

В версии .NET, которую я использую, вы также должны сделать это:

System.Net.ServicePointManager.Expect100Continue = false;

Если вы нет, класс HttpWebRequest автоматически добавит заголовок запроса Expect: 100-continue , который все портит.

Также я на собственном горьком опыте узнал, что у вас должен быть нужное количество тире. все, что вы говорите, является «границей» в заголовке Content-Type , должно предшествовать два дефиса

--THEBOUNDARY

и в конце

--THEBOUNDARY--

точно так же, как в примере кода. Если ваша граница состоит из множества дефисов, за которыми следует число, эта ошибка не будет очевидна при просмотре http-запроса на прокси-сервере

9
ответ дан 7 November 2019 в 09:01
поделиться

Небольшая оптимизация класса раньше. В этой версии файлы не загружаются в память полностью.

Совет по безопасности: отсутствует проверка границ, если файл содержит границу, произойдет сбой.

namespace WindowsFormsApplication1
{
    public static class FormUpload
    {
        private static string NewDataBoundary()
        {
            Random rnd = new Random();
            string formDataBoundary = "";
            while (formDataBoundary.Length < 15)
            {
                formDataBoundary = formDataBoundary + rnd.Next();
            }
            formDataBoundary = formDataBoundary.Substring(0, 15);
            formDataBoundary = "-----------------------------" + formDataBoundary;
            return formDataBoundary;
        }

        public static HttpWebResponse MultipartFormDataPost(string postUrl, IEnumerable<Cookie> cookies, Dictionary<string, string> postParameters)
        {
            string boundary = NewDataBoundary();

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(postUrl);

            // Set up the request properties
            request.Method = "POST";
            request.ContentType = "multipart/form-data; boundary=" + boundary;
            request.UserAgent = "PhasDocAgent 1.0";
            request.CookieContainer = new CookieContainer();

            foreach (var cookie in cookies)
            {
                request.CookieContainer.Add(cookie);
            }

            #region WRITING STREAM
            using (Stream formDataStream = request.GetRequestStream())
            {
                foreach (var param in postParameters)
                {
                    if (param.Value.StartsWith("file://"))
                    {
                        string filepath = param.Value.Substring(7);

                        // 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,
                            Path.GetFileName(filepath) ?? param.Key,
                            MimeTypes.GetMime(filepath));

                        formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, header.Length);

                        // Write the file data directly to the Stream, rather than serializing it to a string.

                        byte[] buffer = new byte[2048];

                        FileStream fs = new FileStream(filepath, FileMode.Open);

                        for (int i = 0; i < fs.Length; )
                        {
                            int k = fs.Read(buffer, 0, buffer.Length);
                            if (k > 0)
                            {
                                formDataStream.Write(buffer, 0, k);
                            }
                            i = i + k;
                        }
                        fs.Close();
                    }
                    else
                    {
                        string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n",
                            boundary,
                            param.Key,
                            param.Value);
                        formDataStream.Write(Encoding.UTF8.GetBytes(postData), 0, postData.Length);
                    }
                }
                // Add the end of the request
                byte[] footer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
                formDataStream.Write(footer, 0, footer.Length);
                request.ContentLength = formDataStream.Length;
                formDataStream.Close();
            }
            #endregion

            return request.GetResponse() as HttpWebResponse;
        }
    }
}
2
ответ дан 7 November 2019 в 09:01
поделиться

Спасибо за код, он сэкономил мне много времени (включая ошибку Except100!).

В любом случае, я обнаружил ошибку в коде, здесь:

formDataStream.Write(encoding.GetBytes(postData), 0, postData.Length);

Если ваши данные POST - это utf-16, postData.Length вернет количество символов, а не количество байтов. Это приведет к усечению публикуемых данных (например, если у вас есть 2 символа, которые закодированы как utf-16, они занимают 4 байта, но postData.Length скажет, что это занимает 2 байта, и вы потеряете 2 последних байта отправленного данные).

Решение - замените эту строку на:

byte[] aPostData=encoding.GetBytes(postData);
formDataStream.Write(aPostData, 0, aPostData.Length);

Используя это, длина рассчитывается по размеру байта [], а не по размеру строки.

3
ответ дан 7 November 2019 в 09:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: