標籤:style blog http color io os java ar for
上傳下載檔案在瀏覽器裡面都非常簡單,上傳檔案在瀏覽器裡就是幾個標籤,中間到底發生了什麼我們全然不知,而下載檔案更是簡單——下載檔案只需獲得檔案的地址以二進位的方式寫出來就好。最近有個同學想在安卓裡面做一個上傳功能的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(怎麼感覺應該叫做尾碼)
二、換行都是"\r\n"
三、只有最後結束的地方才會多一個首碼,如果是多個檔案上傳,前面的都一樣只有最後一個會多一個首碼
附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 = "\r\n"; 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 }View Code
用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()View Code
(fork this http://alexliyu.blog.163.com/blog/static/162754496201252154412432/)
類比POST上傳檔案