溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android中怎么利用Http實(shí)現(xiàn)圖片上傳和表單提交

發(fā)布時(shí)間:2021-06-26 14:48:10 來(lái)源:億速云 閱讀:642 作者:Leah 欄目:云計(jì)算

這篇文章將為大家詳細(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ì)原理有更加清晰的理解。

web端demo

在Android端上傳圖片總是失敗的情況下,后臺(tái)開(kāi)發(fā)哥們幫忙實(shí)現(xiàn)了web端的請(qǐng)求demo,是可以正常處理請(qǐng)求的,頁(yè)面如下: Android中怎么利用Http實(shí)現(xiàn)圖片上傳和表單提交
選擇文件之后,按瀏覽器的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提交方法。

Http請(qǐng)求頭分析

首先來(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");
Http請(qǐng)求體分析

下面來(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");
完整上傳圖片的java代碼
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)主要原因如下:

  1. Android的多種表單提交方案 有HttpClient、httpmine.jar和HttpURLConnection,前兩種方案,官方已不在推薦,而且很容易出現(xiàn)版本兼容性問(wèn)題。所以需要采用HttpURLConnection,但是這種方案沒(méi)有成型的表單提交接口,所以在上傳圖片時(shí),服務(wù)器對(duì)表單解析很容易出問(wèn)題。

  2. 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ò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI