Android 文件上传(客户端+服务端)

前言:

本文介绍的是android HttpUrlConnection实现的文件上传,接触过web的,大家都知道表单文件上传我们只需要指定enctype="multipart/form-data" method="post"就可以了。这是因为浏览器浏览器已经帮我构建了消息头,而在android中我们需要自己拼接复杂的消息头,来实现文件上传。本人的第一篇博文,希望大家多多支持!!!

Android端实现:

下面是android端文件上传的主要方法,我们可以 传递普通数据和文件,参数通过params和files已经加以区分了,同时可以一次上传多个文件。

public static String uploadFile(String url, Map<String, String> params,
			Map<String, File> files) throws IOException {
		String result = "";

		String BOUNDARY = java.util.UUID.randomUUID().toString();
		String PREFIX = "--", LINEND = "\r\n";
		String MULTIPART_FROM_DATA = "multipart/form-data";
		String CHARSET = "UTF-8";

		URL uri = new URL(url);
		HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
		conn.setReadTimeout(5 * 1000); // 缓存的最长时间
		conn.setDoInput(true);// 允许输入
		conn.setDoOutput(true);// 允许输出
		conn.setUseCaches(false); // 不允许使用缓存
		conn.setRequestMethod("POST");
		conn.setRequestProperty("connection", "keep-alive");
		conn.setRequestProperty("Charsert", "UTF-8");
		conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA
				+ ";boundary=" + BOUNDARY);

		// 首先发送普通数据
		StringBuilder sb = new StringBuilder();
		for (Map.Entry<String, String> entry : params.entrySet()) {
			sb.append(PREFIX);
			sb.append(BOUNDARY);
			sb.append(LINEND);
			sb.append("Content-Disposition: form-data; name=\""
					+ entry.getKey() + "\"" + LINEND);
			sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND);
			sb.append("Content-Transfer-Encoding: 8bit" + LINEND);
			sb.append(LINEND);
			sb.append(entry.getValue());
			sb.append(LINEND);
		}

		DataOutputStream outStream = new DataOutputStream(
				conn.getOutputStream());
		outStream.write(sb.toString().getBytes("UTF-8"));
		// 发送文件数据
		if (files != null)
			for (Map.Entry<String, File> file : files.entrySet()) {
				StringBuilder sb1 = new StringBuilder();
				sb1.append(PREFIX);
				sb1.append(BOUNDARY);
				sb1.append(LINEND);
				sb1.append("Content-Disposition: form-data; name=\"file\"; filename=\""
						+ file.getKey() + "\"" + LINEND);
				sb1.append("Content-Type: application/octet-stream; charset="
						+ CHARSET + LINEND);
				sb1.append(LINEND);
				outStream.write(sb1.toString().getBytes("UTF-8"));

				InputStream is = new FileInputStream(file.getValue());
				byte[] buffer = new byte[1024];
				int len = 0;
				while ((len = is.read(buffer)) != -1) {
					outStream.write(buffer, 0, len);
				}

				is.close();
				outStream.write(LINEND.getBytes());
			}

		// 请求结束标志
		byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();
		outStream.write(end_data);
		outStream.flush();
		// 得到响应码
		int res = conn.getResponseCode();
		InputStream in = conn.getInputStream();
		if (res == 200) {
			int ch;
			StringBuilder sb2 = new StringBuilder();
			while ((ch = in.read()) != -1) {
				sb2.append((char) ch);
			}
			result = sb2.toString();
		}
		outStream.close();
		conn.disconnect();
		return result;

	}
首先,看代码。我们构造了消息头。对于消息头的格式大家可以使用抓包工具,来抓取。然后我们设置一些连接属性,分隔符等,最后开始传递普通数据以及文件,代码的注释也比较详细,这里不过多说明了。调用的代码接下来。由于是耗时操作,我使用了AsyncTask来实现异步,具体的逻辑,界面更新大家可以更加需求,自己实现代码。
public void uploadFile(String url, Map<String, String> normalMap,
			Map<String, File> files) {
		new AsyncTask<Object, Void, String>() {

			@Override
			protected String doInBackground(Object... params) {
				// TODO Auto-generated method stub
				String url = (String) params[0];
				String result = null;
				Map<String, String> normalMap = (Map<String, String>) params[1];
				Map<String, File> files = (Map<String, File>) params[2];
				try {
					result =  HttpUtil.uploadFile(url, normalMap, files);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				return result;
			}

			@Override
			protected void onPostExecute(String result) {
				// TODO Auto-generated method stub
				super.onPostExecute(result);
				//实现界面更新
			}

		}.execute(new Object[] { url, normalMap, files });

	}

服务端:

服务端本人是通过java web servlet来实现的,主要借助了apache的 commons-fileupload-1.2.1.jar,关于文件上传的jar包大家可以网上下载,下面见主要代码
public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		
		DataOutputStream outputStream = new DataOutputStream(response.getOutputStream());
		// 上传文件的目录,放在WEB-INF下面,不允许外界直接访问
		String uploadPath = getServletContext().getRealPath("/WEB-INF/upload");
		// 临时文件目录
		String tempPath = getServletContext().getRealPath("/WEB-INF/temp");
		File tmpFile = new File(tempPath);
		if (!tmpFile.exists()) {
			// 创建临时目录
			tmpFile.mkdir();
		}
		try {
			// Create a factory for disk-based file items
			DiskFileItemFactory factory = new DiskFileItemFactory();
			// 设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。默认10KB
			factory.setSizeThreshold(1024 * 100);
			factory.setRepository(tmpFile);// 设置缓冲区目录
			ServletFileUpload upload = new ServletFileUpload(factory);
			// 解决上传文件名的中文乱码
			upload.setHeaderEncoding("UTF-8");
			// 设置单个最大文件尺寸,这里是4MB
			upload.setFileSizeMax(4 * 1024 * 1024);
			// 设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为10MB
			upload.setSizeMax(1024 * 1024 * 10);

			// 监听文件上传进度
			upload.setProgressListener(new ProgressListener() {
				public void update(long pBytesRead, long pContentLength,
						int arg2) {
					System.out.println("文件大小为:" + pContentLength + ",已上传:"
							+ pBytesRead);
				}
			});
			// 3、判断提交上来的数据是否是上传表单的数据
			if (!ServletFileUpload.isMultipartContent(request)) {
				// 按照传统方式获取数据
				return;
			}
			List<FileItem> items = upload.parseRequest(request);// 解析封装请求

			for (FileItem item : items) {

				if (item.isFormField()) {// 普通数据
					String name = item.getFieldName();
					// 解决普通输入项的数据的中文乱码问题
					String value = item.getString("UTF-8");
					System.out.println(name + "=" + value);
				} else {// 文件类型
					String filename = item.getName();
					if (filename == null || filename.trim().equals("")) {
						continue;
					}
					// 不同的浏览器提交的文件名是不一样的,只保留文件名部分
					filename = filename
							.substring(filename.lastIndexOf("\\") + 1);
					// 得到上传文件的扩展名
					String fileExtName = filename.substring(filename
							.lastIndexOf(".") + 1);
					// 如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法
					System.out.println("上传的文件的扩展名是:" + fileExtName);
					// 获取item中的上传文件的输入流
					InputStream in = item.getInputStream();
					// 得到文件保存的名称
					String saveFilename = makeFileName(filename);
					// 得到文件的保存目录
					String realSavePath = makePath(saveFilename, uploadPath);
					// 创建一个文件输出流
					FileOutputStream out = new FileOutputStream(realSavePath
							+ "\\" + saveFilename);
					// 创建一个缓冲区
					byte buffer[] = new byte[1024];
					// 判断输入流中的数据是否已经读完的标识
					int len = 0;
					// 循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
					while ((len = in.read(buffer)) > 0) {
						out.write(buffer, 0, len);
					}
					// 关闭输入流
					in.close();
					// 关闭输出流
					out.close();
					// 删除处理文件上传时生成的临时文件
					item.delete();
					
					outputStream.writeBytes("YES");
				}
			}
			System.out.print("upload succeed");
		} catch (FileUploadBase.FileSizeLimitExceededException e) {
			e.printStackTrace();
			System.out.println("-------------单个文件超出最大值----------------");
			return;
		} catch (FileUploadBase.SizeLimitExceededException e) {
			e.printStackTrace();
			System.out
					.println("--------------上传文件的总的大小超出限制的最大值----------------");
			return;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
代码里面的注释已经很详细了,大家可以看看。最后别忘了在web.xml里面配置我们的servlet,ok,至此一个简单的文件上传功能就基本完成了。

总结:

HttpClient类好像已经被google弃用了,使用原生的HttpUrlConnection感觉实现起来还是过于繁琐,大家可以试着使用一些开源框架okhttp、xutils、volley等,实现起来很简单,稳定性也比较高,以后有机会再一起学习,see you!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值