Android程序開(kāi)發(fā)通過(guò)HttpURLConnection上傳文件到服務(wù)器
一:實(shí)現(xiàn)原理
最近在做Android客戶端的應(yīng)用開(kāi)發(fā),涉及到要把圖片上傳到后臺(tái)服務(wù)器中,自己選擇了做Spring3 MVC HTTP API作為后臺(tái)上傳接口,android客戶端我選擇用HttpURLConnection來(lái)通過(guò)form提交文件數(shù)據(jù)實(shí)現(xiàn)上傳功能,本來(lái)想網(wǎng)上搜搜拷貝一下改改代碼就好啦,發(fā)現(xiàn)根本沒(méi)有現(xiàn)成的例子,多數(shù)的例子都是基于HttpClient的或者是基于Base64編碼以后作為字符串來(lái)傳輸圖像數(shù)據(jù),于是我不得不自己動(dòng)手,參考了網(wǎng)上一些資料,最終實(shí)現(xiàn)基于HttpURLConnection上傳文件的android客戶端代碼,廢話少說(shuō),其實(shí)基于HttpURLConnection實(shí)現(xiàn)文件上傳最關(guān)鍵的在于要熟悉Http協(xié)議相關(guān)知識(shí),知道MIME文件塊在Http協(xié)議中的格式表示,基本的傳輸數(shù)據(jù)格式如下:
其中boundary表示form的邊界,只要按照格式把內(nèi)容字節(jié)數(shù)寫(xiě)到HttpURLConnection的對(duì)象輸出流中,服務(wù)器端的Spring Controller 就會(huì)自動(dòng)響應(yīng)接受,跟從瀏覽器頁(yè)面上上傳文件是一樣的。
服務(wù)器端HTTP API, 我是基于Spring3 MVC實(shí)現(xiàn)的Controller,代碼如下:
@RequestMapping(value = "/uploadMyImage/{token}", method = RequestMethod.POST)
public @ResponseBody String getUploadFile(HttpServletRequest request, HttpServletResponse response,
@PathVariable String token) {
logger.info("spring3 MVC upload file with Multipart form");
logger.info("servlet context path : " + request.getSession().getServletContext().getRealPath("/"));
UserDto profileDto = userService.getUserByToken(token);
String imgUUID = "";
try {
if (request instanceof MultipartHttpServletRequest && profileDto.getToken() != null) {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
logger.info("spring3 MVC upload file with Multipart form");
// does not work, oh my god!!
MultipartFile file = multipartRequest.getFiles("myfile").get(0);
InputStream input = file.getInputStream();
long fileSize = file.getSize();
BufferedImage image = ImageIO.read(input);
// create data transfer object
ImageDto dto = new ImageDto();
dto.setCreateDate(new Date());
dto.setFileName(file.getOriginalFilename());
dto.setImage(image);
dto.setCreator(profileDto.getUserName());
dto.setFileSize(fileSize);
dto.setType(ImageAttachmentType.CLIENT_TYPE.getTitle());
dto.setUuid(UUID.randomUUID().toString());
/// save to DB
imgUUID = imageService.createImage(dto);
input.close();
}
} catch (Exception e) {
e.printStackTrace();
logger.error("upload image error", e);
}
return imgUUID;
}
Android客戶端基于HttpURLConnection實(shí)現(xiàn)上傳的代碼,我把它封裝成一個(gè)單獨(dú)的類文件,這樣大家可以直接使用,只要傳入上傳的URL等參數(shù)即可。代碼如下:
package com.demo.http;
import java.io.BufferedInputStream;
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.util.Random;
import android.os.Handler;
import android.util.Base64;
import android.util.Log;
public class UploadImageTask implements APIURLConstants {
private String requestURL = DOMAIN_ADDRESS + UPLOAD_DESIGN_IMAGE_URL; // default
private final String CRLF = "\r\n";
private Handler handler;
private String token;
public UploadImageTask(String token, Handler handler) {
this.handler = handler;
this.token = token;
}
public String execute(File...files) {
InputStream inputStream = null;
HttpURLConnection urlConnection = null;
FileInputStream fileInput = null;
DataOutputStream requestStream = null;
handler.sendEmptyMessage(50);
try {
// open connection
URL url = new URL(requestURL.replace("{token}", this.token));
urlConnection = (HttpURLConnection) url.openConnection();
// create random boundary
Random random = new Random();
byte[] randomBytes = new byte[16];
random.nextBytes(randomBytes);
String boundary = Base64.encodeToString(randomBytes, Base64.NO_WRAP);
/* for POST request */
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setUseCaches(false);
urlConnection.setRequestMethod("POST");
long size = (files[0].length() / 1024);
if(size >= 1000) {
handler.sendEmptyMessage(-150);
return "error";
}
// 構(gòu)建Entity form
urlConnection.setRequestProperty("Connection", "Keep-Alive");
urlConnection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
urlConnection.setRequestProperty("Cache-Control", "no-cache");
// never try to chunked mode, you need to set a lot of things
// if(size > 400) {
// urlConnection.setChunkedStreamingMode(0);
// }
// else {
// urlConnection.setFixedLengthStreamingMode((int)files[0].length());
// }
// end comment by zhigang on 2016-01-19
/* upload file stream */
fileInput = new FileInputStream(files[0]);
requestStream = new DataOutputStream(urlConnection.getOutputStream());
String nikeName = "myfile";
requestStream = new DataOutputStream(urlConnection.getOutputStream());
requestStream.writeBytes("--" + boundary + CRLF);
requestStream.writeBytes("Content-Disposition: form-data; name=\"" + nikeName + "\"; filename=\"" + files[0].getName() + "\""+ CRLF);
requestStream.writeBytes("Content-Type: " + getMIMEType(files[0]) + CRLF);
requestStream.writeBytes(CRLF);
// 寫(xiě)圖像字節(jié)內(nèi)容
int bytesRead;
byte[] buffer = new byte[8192];
handler.sendEmptyMessage(50);
while((bytesRead = fileInput.read(buffer)) != -1) {
requestStream.write(buffer, 0, bytesRead);
}
requestStream.flush();
requestStream.writeBytes(CRLF);
requestStream.flush();
requestStream.writeBytes("--" + boundary + "--" + CRLF);
requestStream.flush();
fileInput.close();
// try to get response
int statusCode = urlConnection.getResponseCode();
if (statusCode == 200) {
inputStream = new BufferedInputStream(urlConnection.getInputStream());
String imageuuId = HttpUtil.convertInputStreamToString(inputStream);
Log.i("image-uuid", "uploaded image uuid : " + imageuuId);
handler.sendEmptyMessage(50);
return imageuuId;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(requestStream != null) {
try {
requestStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fileInput != null) {
try {
fileInput.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (urlConnection != null) {
urlConnection.disconnect();
}
}
handler.sendEmptyMessage(50);
return null;
}
private String getMIMEType(File file) {
String fileName = file.getName();
if(fileName.endsWith("png") || fileName.endsWith("PNG")) {
return "image/png";
}
else {
return "image/jpg";
}
}
}
經(jīng)過(guò)本人測(cè)試,效果杠杠的??!所以請(qǐng)忘記HttpClient這個(gè)東西,android開(kāi)發(fā)再也不需要它了。
- Android HttpURLConnection下載網(wǎng)絡(luò)圖片設(shè)置系統(tǒng)壁紙
- Android 用HttpURLConnection訪問(wèn)網(wǎng)絡(luò)的方法
- Android基于HttpUrlConnection類的文件下載實(shí)例代碼
- Android網(wǎng)絡(luò)技術(shù)HttpURLConnection詳解
- Android 中HttpURLConnection與HttpClient使用的簡(jiǎn)單實(shí)例
- Android HttpURLConnection.getResponseCode()錯(cuò)誤解決方法
- Android使用HttpURLConnection實(shí)現(xiàn)網(wǎng)絡(luò)訪問(wèn)流程
相關(guān)文章
詳解Flutter自定義應(yīng)用程序內(nèi)鍵盤(pán)的實(shí)現(xiàn)方法
本文將展示如何利用Flutter創(chuàng)建自定義鍵盤(pán)小部件,用于在自己的應(yīng)用程序中的Flutter TextField中輸入文本,感興趣的小伙伴可以了解一下2022-06-06
Kotlin中Lambda表達(dá)式與高階函數(shù)使用分析講解
lambda 本質(zhì)上是可以傳遞給函數(shù)的一小段代碼,Kotlin 與 Java 中的 Lambda 有一定的區(qū)別,除了對(duì) lambda 的全面支持外,還有內(nèi)聯(lián)函數(shù)等簡(jiǎn)潔高效的特性。下面我們來(lái)仔細(xì)看一下2022-12-12
Android 仿摩拜單車共享單車進(jìn)度條實(shí)現(xiàn)StepView效果
這篇文章主要介紹了android 仿摩拜單車共享單車進(jìn)度條實(shí)現(xiàn)StepView效果的實(shí)例,通過(guò)定義五個(gè)狀態(tài),分別為:為完成、正在進(jìn)行、已完成、終點(diǎn)完成、終點(diǎn)未完成。具體實(shí)現(xiàn)代碼,大家參考下2017-03-03
Android編程實(shí)現(xiàn)二維碼的生成與解析
這篇文章主要介紹了Android編程實(shí)現(xiàn)二維碼的生成與解析方法,結(jié)合實(shí)例分析了Android二維碼的生成與讀取二維碼的相關(guān)技巧,并提供了二維碼jar包供讀者下載,需要的朋友可以參考下2015-11-11
使用Docker來(lái)加速構(gòu)建Android應(yīng)用的基本部署思路解析
這篇文章主要介紹了使用Docker來(lái)加速構(gòu)建Android應(yīng)用的部署思路解析,在服務(wù)器中通過(guò)Docker鏡像來(lái)獲得更高效的開(kāi)發(fā)和測(cè)試流程,需要的朋友可以參考下2016-01-01
Android開(kāi)心消消樂(lè)代碼實(shí)例詳解
這篇文章主要介紹了Android開(kāi)心消消樂(lè)代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
Android自定義PopupWindow實(shí)現(xiàn)炫酷的IOS對(duì)話框效果
這篇文章主要給大家介紹如何在android中實(shí)現(xiàn)高仿ios對(duì)話框效果,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-05-05

