Android端通过HttpURLConnection上传文件到服务器

简介: Android端通过HttpURLConnection上传文件到服务器一:实现原理最近在做Android客户端的应用开发,涉及到要把图片上传到后台服务器中,自己选择了做Spring3 MVC HTTP API作为后台上传接口,android客户端我选择用HttpURLConnection来通...

Android端通过HttpURLConnection上传文件到服务器

一:实现原理

最近在做Android客户端的应用开发,涉及到要把图片上传到后台服务器中,自己选择了做Spring3 MVC HTTP API作为后台上传接口,android客户端我选择用HttpURLConnection来通过form提交文件数据实现上传功能,本来想网上搜搜拷贝一下改改代码就好啦,发现根本没有现成的例子,多数的例子都是基于HttpClient的或者是基于Base64编码以后作为字符串来传输图像数据,于是我不得不自己动手,参考了网上一些资料,最终实现基于HttpURLConnection上传文件的android客户端代码,废话少说,其实基于HttpURLConnection实现文件上传最关键的在于要熟悉Http协议相关知识,知道MIME文件块在Http协议中的格式表示,基本的传输数据格式如下:


其中boundary表示form的边界,只要按照格式把内容字节数写到HttpURLConnection的对象输出流中,服务器端的Spring Controller 就会自动响应接受,跟从浏览器页面上上传文件是一样的。

服务器端HTTP API, 我是基于Spring3 MVC实现的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实现上传的代码,我把它封装成一个单独的类文件,这样大家可以直接使用,只要传入上传的URL等参数即可。代码如下:

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";
			}
			// 构建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);
			// 写图像字节内容
			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";
		}
	}

}
经过本人测试,效果杠杠的!!所以请忘记HttpClient这个东西,android开发再也不需要它了。


目录
相关文章
|
2月前
|
Java Android开发 C++
Android Studio JNI 使用模板:c/cpp源文件的集成编译,快速上手
本文提供了一个Android Studio中JNI使用的模板,包括创建C/C++源文件、编辑CMakeLists.txt、编写JNI接口代码、配置build.gradle以及编译生成.so库的详细步骤,以帮助开发者快速上手Android平台的JNI开发和编译过程。
93 1
|
4月前
|
存储 数据库 Android开发
安卓Jetpack Compose+Kotlin,支持从本地添加音频文件到播放列表,支持删除,使用ExoPlayer播放音乐
为了在UI界面添加用于添加和删除本地音乐文件的按钮,以及相关的播放功能,你需要实现以下几个步骤: 1. **集成用户选择本地音乐**:允许用户从设备中选择音乐文件。 2. **创建UI按钮**:在界面中创建添加和删除按钮。 3. **数据库功能**:使用Room数据库来存储音频文件信息。 4. **更新ViewModel**:处理添加、删除和播放音频文件的逻辑。 5. **UI实现**:在UI层支持添加、删除音乐以及播放功能。
|
2月前
|
开发工具 git 索引
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
57 1
|
2月前
|
存储 监控 数据库
Android经典实战之OkDownload的文件分段下载及合成原理
本文介绍了 OkDownload,一个高效的 Android 下载引擎,支持多线程下载、断点续传等功能。文章详细描述了文件分段下载及合成原理,包括任务创建、断点续传、并行下载等步骤,并展示了如何通过多种机制保证下载的稳定性和完整性。
35 0
|
4月前
|
缓存 Android开发 Kotlin
【安卓app开发】kotlin Jetpack Compose框架 | 先用OKhttp下载远程音频文件再使用ExoPlayer播放
使用 Kotlin 的 Jetpack Compose 开发安卓应用时,可以结合 OkHttp 下载远程音频文件和 ExoPlayer 进行播放。在 `build.gradle` 添加相关依赖后,示例代码展示了如何下载音频并用 ExoPlayer 播放。代码包括添加依赖、下载文件、播放文件及简单的 Compose UI。注意,示例未包含完整错误处理和资源释放,实际应用需补充这些内容。
|
4月前
|
存储 Android开发 Kotlin
开发安卓app OKhttp下载后使用MediaPlayer播放
在Android Jetpack Compose应用程序中,要使用OkHttp下载远程音频文件并在本地播放,你需要完成以下几个步骤: 1. **添加依赖**:确保`build.gradle`文件包含OkHttp和Jetpack Compose的相关依赖。 2. **下载逻辑**:创建一个`suspend`函数,使用OkHttp发起网络请求下载音频文件到本地。 3. **播放逻辑**:利用`MediaPlayer`管理音频播放状态。 4. **Compose UI**:构建用户界面,包含下载和播放音频的按钮。
|
4月前
|
JSON 编解码 Apache
Android中使用HttpURLConnection实现GET POST JSON数据与下载图片
Android中使用HttpURLConnection实现GET POST JSON数据与下载图片
45 1
|
4月前
|
Java 开发工具 Android开发
详细解读Android开发DNK开发将.c文件打包成os
详细解读Android开发DNK开发将.c文件打包成os
25 0
|
4月前
|
Android开发
Android Gradle开发—脚本实现自动打包后复制一份APK文件,并修改APK名称,到指定目录作备份
Android Gradle开发—脚本实现自动打包后复制一份APK文件,并修改APK名称,到指定目录作备份
170 0
|
4月前
|
缓存 大数据 Android开发
Android 巧用putBinder方法传递大文件
Android 巧用putBinder方法传递大文件
87 0
下一篇
无影云桌面