上传下载文件在浏览器里面都非常简单,上传文件在浏览器里就是几个标签,中间到底发生了什么我们全然不知,而下载文件更是简单——下载文件只需获得文件的地址以二进制的方式写出来就好。最近有个同学想在安卓里面做一个上传功能的App,最简单的莫过于HTTP的POST方法了(fork this https://www.imququ.com/post/four-ways-to-post-data-in-http.html)。
可以先观察下上传文件的HTTP Header:
可以看到Request Header后面多了一个没有见过的 Request Payload, 这个Request Payload 正是上传文件的关键,我的理解主要参考 http://jackface.iteye.com/blog/2098604,这篇文章详细的介绍了他在后台抓取到的POST的信息的格式,对,POST传送文件的时候格式一定要严格,否则就上传不了(我已经做过多次试验),文章里抓到的数据和 Request Payload里的数据差不多,知识Payload里面没有文件的二进制流。
总结一下,POST上传文件的要点:
一、boundary是浏览器随机生成的分隔符(请求头里面有),请求头里面的boundary前面是四个"-",而Payload里面的是六个,多的两个叫做 prefix(前缀),然后就是结束的地方又有两个prefix(怎么感觉应该叫做后缀)
二、换行都是" "
三、只有最后结束的地方才会多一个前缀,如果是多个文件上传,前面的都一样只有最后一个会多一个前缀
附java实现代码(照着网上的改的):
1 import java.io.ByteArrayOutputStream; 2 import java.io.File; 3 import java.io.FileInputStream; 4 import java.io.FileNotFoundException; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.OutputStream; 8 import java.net.HttpURLConnection; 9 import java.net.MalformedURLException; 10 import java.net.URL; 11 12 public class Poster 13 { 14 static String boundary = "--------------7d226f700d0"; 15 static String prefix = "--"; 16 static String newLine = " "; 17 public static void main(String args[]) 18 { 19 test(); 20 } 21 private static void test() 22 { 23 try 24 { 25 26 URL url = new URL("http://1.crackme.sinaapp.com/scripts/upload.php");//post address 27 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 28 connection.setDoInput(true); 29 connection.setDoOutput(true); 30 31 connection.setRequestProperty("Content-type","multipart/form-data;boundary="+boundary); 32 AssemblyHttp(connection.getOutputStream()); 33 InputStream ins = connection.getInputStream(); 34 byte[] b = readBuffer(ins); 35 System.out.println(new String(b)); 36 } catch (MalformedURLException e) 37 { 38 System.out.println(" URL address error "); 39 } catch (IOException e) 40 { 41 System.out.println(" URL open error "); 42 } 43 } 44 private static void AssemblyHttp(OutputStream out) 45 { 46 StringBuffer params = new StringBuffer(); 47 48 params.append(prefix+boundary + newLine); 49 //name is the the key "name"'s value in the upload form 50 params.append("Content-Disposition: form-data; name="myfile[]";filename="+""20140821182141.jpg""); 51 params.append(newLine); 52 params.append("Content-Type: image/jpeg"); 53 params.append(newLine+newLine); 54 55 StringBuffer params2 = new StringBuffer(); 56 57 params2.append(prefix+boundary + newLine); 58 59 params2.append("Content-Disposition: form-data; name="myfile[]";filename="+""6b68a43308f198a375d9369a39c7ee1e.jpg""); 60 params2.append(newLine); 61 params2.append("Content-Type: image/jpeg"); 62 params2.append(newLine+newLine); 63 64 65 File file = new File("C:\Users\KL\Pictures\20140821182141.jpg"); 66 File file2 = new File("C:\Users\KL\Pictures\6b68a43308f198a375d9369a39c7ee1e.jpg"); 67 try 68 { 69 InputStream in = new FileInputStream(file); 70 out.write(params.toString().getBytes()); 71 out.write(readBuffer(in)); 72 out.write(newLine.getBytes()); 73 out.write((prefix+boundary+newLine).getBytes()); 74 75 76 InputStream in2 = new FileInputStream(file2); 77 out.write(params2.toString().getBytes()); 78 out.write(readBuffer(in2)); 79 out.write(newLine.getBytes()); 80 81 out.write((prefix+boundary + prefix).getBytes()); 82 out.flush(); 83 out.close(); 84 } catch (FileNotFoundException e) 85 { 86 System.out.println(" file not found "); 87 } catch (IOException e) 88 { 89 System.out.println(" IO Error "); 90 } 91 } 92 93 94 public static byte[] readBuffer(InputStream ins) throws IOException 95 { 96 byte b[] = new byte[1024]; 97 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 98 int len = 0; 99 while((len=ins.read(b))!= -1) 100 { 101 stream.write(b, 0, len); 102 } 103 return stream.toByteArray(); 104 } 105 }
用java写可以看到好多上传的细节,用Python的第三方库就非常简洁了,libcurl(fork this http://curl.haxx.se/libcurl/)有python的移植但是有点太庞大的,配置起来也不好配置,poster 也是python的一个第三方库,用起来非常方便,代码如下:
1 # test_client.py 2 from poster.encode import multipart_encode 3 from poster.streaminghttp import register_openers 4 import urllib2 5 6 url = ["http://1.crackme.sinaapp.com/scripts/upload.php","http://localhost/learnPHP/scripts/upload.php"] 7 8 register_openers() 9 10 11 datagen, headers = multipart_encode({"myfile": open("C:\Users\KL\Pictures\20140821182141.jpg", "rb")}) 12 13 14 request = urllib2.Request(url[0], datagen, headers) 15 16 print urllib2.urlopen(request).read()
(fork this http://alexliyu.blog.163.com/blog/static/162754496201252154412432/)