.Net实现表单上传

由于WinPhone开发,需要同时上传多个文件,然后发现一直被H4x奉为好用的WebClient,的UploadFileAsync 不支持多文件上传,但我服务器端不支持 Upload file one by one. 于是只能自己实现了。

实现起来异常的痛苦啊,因为开始不知道有RFC1867这个东西,就被迫一点点的抓包分析,看的晕晕乎乎。之后在StackOverFlow上。了解到,原来这东西是有协议的。

RFC 1867

原文可以看这里

这里摘抄一下它的demo

1
2
3
4
5
6
7
8
9
10
11
12
  Content-type: multipart/form-data, boundary=AaB03x

    --AaB03x
    content-disposition: form-data; name="field1"

    Joe Blow
    --AaB03x
    content-disposition: form-data; name="pics"; filename="file1.txt"
    Content-Type: text/plain

     ... contents of file1.txt ...
    --AaB03x--

1.要定义Content-type: 是 multipart/form-data

2.boundary 这个东西很重要,起到多个post value 之间的分隔符的作用。

3.之后要严格以下文这种格式区分每段。

1
2
3
4
--boundary
content-disposition: form-data; name="Kitty"
      
I Love You

4.遇到文件类型的话 不仅要写上 content-disposition: form-data; name=“Kitty” 同时还要写上 filename = “kitty.png”。

5.在最后一定要注意。结束符并不是 —boundary 而是 —boundary—

6.一般boundary都是随机生成的超长字符串。以防出现意外情况。

.Net 实现

实现起来并不复杂,只是作为一个对Cocoa 死忠的脑残水果粉,好多库文件我不会使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class PostDataWrapper
  {
      private Dictionary<string, string> postparms;
      private Dictionary<string, string> uploadFilePath;
      private String boundary;
      bool    dirtyFlag;
      byte[]   postData;

      public PostDataWrapper ()
      {
          postparms = new Dictionary<string, string> ();
          uploadFilePath = new Dictionary<string, string> ();
          dirtyFlag = false;
          randomBoundary ();
      }

      private void randomBoundary()
      {
          boundary = "0xKhTmLbOuNdArY-B8EC3DCB-C682-4050-98D0-C294B4BB9A0E";
      }

      public String ContentType()
      {
          return string.Format("multipart/form-data; charset=utf-8; boundary="+ boundary);
      }

      public void AddPostValue(String key, String Value)
      {
          postparms.Add (key, Value);
          dirtyFlag = true;
      }

      public void AddPostFile(String key, String Path)
      {
          uploadFilePath.Add (key, Path);
          dirtyFlag = true;
      }

      public byte[] GetPostData()
      {
          if (dirtyFlag) {
              randomBoundary ();
      
              List<byte> _postBody = new List<byte> ();
              UTF8Encoding encoding = new UTF8Encoding ();

              foreach (string str in postparms.Keys) {
                  string item = string.Format ("--" + boundary + "\nContent-Disposition: form-data; name=\""+ str + "\"\n\n" + postparms [str]+ "\n");
                  _postBody.AddRange (encoding.GetBytes(item));
              }

              foreach (string str in uploadFilePath.Keys) {
                  string[] str_arr = uploadFilePath[str].Split('/');

                  string item = string.Format ("--" + boundary + "\nContent-Disposition: form-data; name=\""+ str + "\"; filename=\"" + str_arr[str_arr.Length - 1 ] +"\"" + " \n\n");
                  _postBody.AddRange (encoding.GetBytes(item));
                  _postBody.AddRange (File.ReadAllBytes( uploadFilePath[str] ));
                  _postBody.AddRange (encoding.GetBytes("\n"));
              }

              _postBody.AddRange (encoding.GetBytes("--" + boundary + "--\n"));

              postData = _postBody.ToArray ();

              dirtyFlag = false;
          }

          return postData;
      }
  }

然后上传部分的代码是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
string url = "http://t-xx.me/iloveyou";
WebClient client = new WebClient ();


PostDataWrapper wrapper = new PostDataWrapper ();
wrapper.AddPostValue("message", "我爱你");
wrapper.AddPostValue("skin", "txx");
wrapper.AddPostFile ("preview_image", "/Users/txx/3.gif");
wrapper.AddPostFile ("image", "/Users/txx/3.jpg");
client.Headers.Add ("Content-Type", wrapper.ContentType ());

client.UploadProgressChanged += (object sender, UploadProgressChangedEventArgs e) => {
  Debug.WriteLine(e.ProgressPercentage.ToString());
};

client.UploadDataCompleted += handler;


client.UploadDataAsync (new Uri(url),"POST", wrapper.GetPostData());

现在不知道是 mono的问题 还是我代码的问题,上传是正常的,但是收不到 progress 这不科学!

Comments