您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)Android中怎么利用Http實(shí)現(xiàn)圖片上傳和表單提交,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
服務(wù)器主要是Web居多,客戶端一般通過(guò)http上傳文件到web服務(wù)器,最開(kāi)始的設(shè)想很簡(jiǎn)單,直接將圖片轉(zhuǎn)化為字節(jié)流,寫入到http的outstream,隨后發(fā)送出去即可。
但當(dāng)這種方法出現(xiàn)問(wèn)題,服務(wù)器根據(jù)文件名這個(gè)表單中的字段來(lái)判定是否接收到文件,我上面那種簡(jiǎn)單的方法從而使得每次服務(wù)器反饋說(shuō)沒(méi)有接收到圖片文件,從而發(fā)送失敗。由此推斷是表單傳輸出了問(wèn)題,Android由于歷史原因,有很多表單傳輸?shù)姆椒?。?dāng)前官方推薦的是HttpURLConnection,但是利用HttpURLConnection構(gòu)建表單的方式,沒(méi)有成型的form封裝方法。比如對(duì)于C#的表單提交,簡(jiǎn)簡(jiǎn)單單幾句話搞定:
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沒(méi)有這么簡(jiǎn)單的封裝形式,需要完整的請(qǐng)求體模擬,用起來(lái)相對(duì)不方便,不過(guò)這樣能夠?qū)翁峤坏谋举|(zhì)原理有更加清晰的理解。
在Android端上傳圖片總是失敗的情況下,后臺(tái)開(kāi)發(fā)哥們幫忙實(shí)現(xiàn)了web端的請(qǐng)求demo,是可以正常處理請(qǐng)求的,頁(yè)面如下:
選擇文件之后,按瀏覽器的F12,便可出現(xiàn)開(kāi)發(fā)者工具界面,在Network一欄可以看到具體的請(qǐng)求和響應(yīng), 分析其請(qǐng)求頭和請(qǐng)求體,來(lái)構(gòu)造Android中相同的參數(shù),就可以實(shí)現(xiàn)文件的正常上傳。 下面就根據(jù)web端的請(qǐng)求demo來(lái)模擬實(shí)現(xiàn)Android的post提交方法。
首先來(lái)看請(qǐng)求的消息頭:
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
根據(jù)請(qǐng)求頭,去構(gòu)建Android的HttpURLConnection相關(guān)參數(shù):
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); // 啟動(dòng)post方法 connection.setRequestMethod("POST"); // 設(shè)置請(qǐng)求頭內(nèi)容 connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundaryCjkbdjmUGD4QJISL");
下面來(lái)分析消息體(Request Payload),內(nèi)容如下:
------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--
從消息體內(nèi)容可以看出,請(qǐng)求消息體本質(zhì)上就是字節(jié)數(shù)組或字符串。內(nèi)容主要分為三部分:
1. 開(kāi)始和結(jié)束字段 開(kāi)始和結(jié)束都有明確的字段 boudary字段的具體內(nèi)容是由消息頭中Content-Type字段進(jìn)行定義的:
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryCjkbdjmUGD4QJISL
這里面設(shè)置的boundary和消息體中的boundary必須保持完全一致,才可以確保消息能夠得到服務(wù)端的正常解析。
2. 表單信息 包含Content-Disposition、name、filename和Content-Type等四個(gè)表單變量,必須要填寫正確的字段,web服務(wù)器才可以對(duì)相關(guān)變量進(jìn)行正確解析
3. 圖片 payload中的亂碼數(shù)據(jù),就是文件的二進(jìn)制表示了
4. 換行回車\r\n 所以Java構(gòu)造payload的原理,就是按照這種順序和特定的字段,進(jìn)行模擬即可,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"); // 取得本地圖片的字節(jié)流,向url流中寫入圖片字節(jié)流 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; // 對(duì)包含中文的字符串進(jìn)行轉(zhuǎn)碼,此為UTF-8。服務(wù)器那邊要進(jìn)行一次解碼 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流中讀寫數(shù)據(jù) connection.setDoInput(true); connection.setDoOutput(true); connection.setUseCaches(true); // 啟動(dòng)post方法 connection.setRequestMethod("POST"); // 設(shè)置請(qǐng)求頭內(nèi)容 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"); // 取得本地圖片的字節(jié)流,向url流中寫入圖片字節(jié)流 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; } }
這個(gè)問(wèn)題花了五個(gè)小時(shí)時(shí)間,花費(fèi)時(shí)間長(zhǎng)主要原因如下:
Android的多種表單提交方案 有HttpClient、httpmine.jar和HttpURLConnection,前兩種方案,官方已不在推薦,而且很容易出現(xiàn)版本兼容性問(wèn)題。所以需要采用HttpURLConnection,但是這種方案沒(méi)有成型的表單提交接口,所以在上傳圖片時(shí),服務(wù)器對(duì)表單解析很容易出問(wèn)題。
chrome的F12工具,requestload中的圖片內(nèi)容看不到,影響了對(duì)圖片http上傳的理解。 最后采用Firefox瀏覽器來(lái)分析請(qǐng)求協(xié)議:
圖片中requestload的內(nèi)容一目了然,所以就知道如何去構(gòu)造圖片+表單提交的request內(nèi)容了,所以這次非常感謝FireFox這種強(qiáng)大的工具,幫忙定位核心問(wèn)題。
關(guān)于Android中怎么利用Http實(shí)現(xiàn)圖片上傳和表單提交就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。