您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Android中怎么利用Http實現圖片上傳和表單提交,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
服務器主要是Web居多,客戶端一般通過http上傳文件到web服務器,最開始的設想很簡單,直接將圖片轉化為字節流,寫入到http的outstream,隨后發送出去即可。
但當這種方法出現問題,服務器根據文件名這個表單中的字段來判定是否接收到文件,我上面那種簡單的方法從而使得每次服務器反饋說沒有接收到圖片文件,從而發送失敗。由此推斷是表單傳輸出了問題,Android由于歷史原因,有很多表單傳輸的方法。當前官方推薦的是HttpURLConnection,但是利用HttpURLConnection構建表單的方式,沒有成型的form封裝方法。比如對于C#的表單提交,簡簡單單幾句話搞定:
WWWForm form = new WWWForm(); form.AddField("frameCount", Time.frameCount.ToString()); form.AddBinaryData("fileUpload", bytes); // Upload to a cgi script WWW w = new WWW("http://localhost/cgi-bin/env.cgi?post", form);
Java的HttpURLConnection沒有這么簡單的封裝形式,需要完整的請求體模擬,用起來相對不方便,不過這樣能夠對單提交的本質原理有更加清晰的理解。
在Android端上傳圖片總是失敗的情況下,后臺開發哥們幫忙實現了web端的請求demo,是可以正常處理請求的,頁面如下:
選擇文件之后,按瀏覽器的F12,便可出現開發者工具界面,在Network一欄可以看到具體的請求和響應, 分析其請求頭和請求體,來構造Android中相同的參數,就可以實現文件的正常上傳。 下面就根據web端的請求demo來模擬實現Android的post提交方法。
首先來看請求的消息頭:
Accept:*/* Accept-Encoding:gzip, deflate Accept-Language:zh-CN,zh;q=0.8,en;q=0.6 Content-Length:38275 Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryCjkbdjmUGD4QJISL Host:118.69.25.90 Origin:http://118.69.25.90 Proxy-Connection:keep-alive Referer:http://118.69.25.90/ User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36 Request Payload
根據請求頭,去構建Android的HttpURLConnection相關參數:
URL url = new URL("http://118.69.25.90/uploadpicture.php"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.setDoOutput(true); connection.setUseCaches(true); // 啟動post方法 connection.setRequestMethod("POST"); // 設置請求頭內容 connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundaryCjkbdjmUGD4QJISL");
下面來分析消息體(Request Payload),內容如下:
------boundary=----WebKitFormBoundaryCjkbdjmUGD4QJISL Content-Disposition: form-data; name="file"; filename="ouba.jpg" Content-Type: image/jpeg ???à 2! !22222222222222222222222222222222222222222222222222?? y?"{qX??Wót??????Riù' ?oh í’?>?&@?Aè"L"?ò2?‘` z?? ??[x ˉ eu”k1??–3???n?ZYT4H?? ′tp???R?)á?1?ê?2?ee—f?]?¢kd?’ú~?’????$ ò-°¢$I?“—>êNw1S ………………………………………… ü?-è w1…a(,R?·Ièa?‘Z~Y? ??7U<???V?+V3.??R?cB?F=…?n2Zò[*?qE?Cg ì????μaCMj?é??Nù?—′ù?2??í′C -|"C?m>ò j5…§?*ùWv?aùúüé?K),G??)ì,Xj? ‰@ ga?AMêraùe ’?7 —Y_′3à^ ???ù ------WebKitFormBoundaryCjkbdjmUGD4QJISL--
從消息體內容可以看出,請求消息體本質上就是字節數組或字符串。內容主要分為三部分:
1. 開始和結束字段 開始和結束都有明確的字段 boudary字段的具體內容是由消息頭中Content-Type字段進行定義的:
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryCjkbdjmUGD4QJISL
這里面設置的boundary和消息體中的boundary必須保持完全一致,才可以確保消息能夠得到服務端的正常解析。
2. 表單信息 包含Content-Disposition、name、filename和Content-Type等四個表單變量,必須要填寫正確的字段,web服務器才可以對相關變量進行正確解析
3. 圖片 payload中的亂碼數據,就是文件的二進制表示了
4. 換行回車\r\n 所以Java構造payload的原理,就是按照這種順序和特定的字段,進行模擬即可,java代碼如下:
DataOutputStreamdos = new DataOutputStream(connection.getOutputStream()); FileInputStream fin = new FileInputStream(filePath); File file = new File(filePath); dos.writeBytes("------boundary=----WebKitFormBoundaryCjkbdjmUGD4QJISL"); dos.writeBytes("\r\n"); dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename="+file.getName()); dos.writeBytes("\r\n"); dos.writeBytes("Content-Type: image/jpeg"); dos.writeBytes("\r\n"); dos.writeBytes("\r\n"); // 取得本地圖片的字節流,向url流中寫入圖片字節流 bytesAvailable = fin.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); buffer = new byte[bufferSize]; bytesRead = fin.read(buffer, 0, bufferSize); while (bytesRead > 0) { dos.write(buffer, 0, bufferSize); bytesAvailable = fin.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); bytesRead = fin.read(buffer, 0, bufferSize); } dos.writeBytes("\r\n"); dos.writeBytes("------WebKitFormBoundaryCjkbdjmUGD4QJISL--"); dos.writeBytes("\r\n"); dos.writeBytes("\r\n");
package com.youreye.tts.qq; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.message.BasicNameValuePair; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; public class UploadPicture { String serverUrl = "http://118.89.25.65/upload-and-detect.php"; HttpURLConnection connection = null; DataOutputStream dos = null; int bytesAvailable, bufferSize, bytesRead; int maxBufferSize = 1 * 1024 * 512; byte[] buffer = null; String boundary = "-----------------------------1954231646874"; Map<String, String> formParams = new HashMap<String, String>(); FileInputStream fin = null; // 對包含中文的字符串進行轉碼,此為UTF-8。服務器那邊要進行一次解碼 private String encode(String value) throws Exception { return URLEncoder.encode(value, "UTF-8"); } public String uploadPicToWebServer(String filePath) { try { URL url = new URL(serverUrl); connection = (HttpURLConnection) url.openConnection(); // 允許向url流中讀寫數據 connection.setDoInput(true); connection.setDoOutput(true); connection.setUseCaches(true); // 啟動post方法 connection.setRequestMethod("POST"); // 設置請求頭內容 connection.setRequestProperty("connection", "Keep-Alive"); connection .setRequestProperty("Content-Type", "multipart/form-data; boundary=---------------------------1954231646874"); dos = new DataOutputStream(connection.getOutputStream()); fin = new FileInputStream(filePath); File file = new File(filePath); dos.writeBytes(boundary); dos.writeBytes("\r\n"); dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename="+file.getName()); dos.writeBytes("\r\n"); dos.writeBytes("Content-Type: image/jpeg"); dos.writeBytes("\r\n"); dos.writeBytes("\r\n"); // 取得本地圖片的字節流,向url流中寫入圖片字節流 bytesAvailable = fin.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); buffer = new byte[bufferSize]; bytesRead = fin.read(buffer, 0, bufferSize); while (bytesRead > 0) { dos.write(buffer, 0, bufferSize); bytesAvailable = fin.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); bytesRead = fin.read(buffer, 0, bufferSize); } dos.writeBytes("\r\n"); dos.writeBytes("-----------------------------1954231646874--"); dos.writeBytes("\r\n"); dos.writeBytes("\r\n"); // Server端返回的信息 int code = connection.getResponseCode(); if (code == 200) { InputStream inStream = connection.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = -1; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } outSteam.close(); inStream.close(); return new String(outSteam.toByteArray()); } if (dos != null) { dos.flush(); dos.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }
這個問題花了五個小時時間,花費時間長主要原因如下:
Android的多種表單提交方案 有HttpClient、httpmine.jar和HttpURLConnection,前兩種方案,官方已不在推薦,而且很容易出現版本兼容性問題。所以需要采用HttpURLConnection,但是這種方案沒有成型的表單提交接口,所以在上傳圖片時,服務器對表單解析很容易出問題。
chrome的F12工具,requestload中的圖片內容看不到,影響了對圖片http上傳的理解。 最后采用Firefox瀏覽器來分析請求協議:
圖片中requestload的內容一目了然,所以就知道如何去構造圖片+表單提交的request內容了,所以這次非常感謝FireFox這種強大的工具,幫忙定位核心問題。
關于Android中怎么利用Http實現圖片上傳和表單提交就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。