本文记录如何使用rfc1867协议来上传文本文件(大小不能超过2M)
首先客户端上传文件时,服务器要收到客户端的请求就必须在客户端人工构造rfc1867协议规定的请求内容。
具体如下:
--UUID
Content-Disposition:form-data;name="字段名"
Content-Type:text/plain;charset=UTF-8 //如果上传的不是文本文件而是图片,则text/plain改为image/jpeg
Content-Transfer-Enconding:8dit //所传送的内容不符合默认的编码方式,须加上该标题头
字段属性值
--UUID
Content-Disposition:form-data;name="字段名";filename="文件名"
Content-Type:application/octet-stream;charset=UTF-8
文件内容(二进制流)
--UUID-- //最后要加上回车
其中UUID是随机生成的一串数字,作为分隔符。
下面是客户端的实现:
public class FileUploadActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); StrictMode .setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads().detectDiskWrites() .detectNetwork().penaltyLog().build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects().penaltyLog() .penaltyDeath().build()); Button button = new Button(this);
button.setText("上传"); setContentView(button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final String actionUrl = "http://36078d58.nat123.cc/FileUploadAndDownload/UploadHandleServlet"; final Map<String, String> params = new HashMap<>(); params.put("strParamName", "strParamValue"); final Map<String, File> files = new HashMap<>(); files.put("android上传.txt", new File("/storage/emulated/0/eguan.txt")); // key为上传后的文件名,value为要上传的文件路径 new Thread(new Runnable() { @Override public void run() { try { String str = post(actionUrl, params, files); Log.d("FileUploadActivity", "结果:" + str); } catch (Exception e) { e.printStackTrace(); } } }).start(); } }); } public static String post(String actionUrl, Map<String, String> params, Map<String, File> files) throws IOException { 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(actionUrl); 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("Charset", "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()); // 发送文件数据 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()); InputStream is = new FileInputStream(file.getValue()); byte[] buffer = new byte[1024]; int len; 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(); StringBuilder sb2 = null; InputStream in = conn.getInputStream(); if (res == 200) { int ch; sb2 = new StringBuilder(); while ((ch = in.read()) != -1) { sb2.append((char) ch); } } outStream.close(); conn.disconnect(); if (sb2==null) return null; else return sb2.toString(); } }
注:
①真机测试
服务端与客户端不在同一个局域网,访问需要外网ip地址映射,如本文为:http://36078d58.nat123.cc/FileUploadAndDownload/UploadHandleServlet;客户端和服务端处在同一个局域网,则可将36078d58.nat123.cc改为服务端所在计算机的局域网ip地址,如192.168.x.x:8080。
②模拟器测试
客户端是android自带的模拟器的,ip改为10.0.2.2:8080;是第三方模拟器的,ip改为服务端所在计算机的ip地址。
下面是服务端的实现:
在eclipse下新建一个web项目,名为FileUploadAndDownload,在src目录下新建一个包,在包内新建一个java文件,名为UploadHandleServlet.java,然后将项目打包成war文件放到Tomcat服务器所在目录下的webapps文件夹内,并启动Tomcat。
@WebServlet("/UploadHandleServlet") public class UploadHandleServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public UploadHandleServlet() { super(); // TODO Auto-generated constructor stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse * response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub // 得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全 String savePath = this.getServletContext().getRealPath("/WEB-INF/upload"); File file = new File(savePath); // 判断上传文件的保存目录是否存在 if (!file.exists() && !file.isDirectory()) { System.out.println(savePath + "目录不存在,需要创建"); // 创建目录 file.mkdir(); } // 消息提示 String message = ""; try { // 使用Apache文件上传组件处理文件上传步骤: // 1、创建一个DiskFileItemFactory工厂 DiskFileItemFactory factory = new DiskFileItemFactory(); // 2、创建一个文件上传解析器 ServletFileUpload upload = new ServletFileUpload(factory); // 解决上传文件名的中文乱码 upload.setHeaderEncoding("UTF-8"); // 3、判断提交上来的数据是否是上传表单的数据 if (!ServletFileUpload.isMultipartContent(request)) { // 按照传统方式获取数据 return; } // 4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项 List<FileItem> list = upload.parseRequest(request); for (FileItem item : list) { // 如果fileitem中封装的是普通输入项的数据 if (item.isFormField()) { String name = item.getFieldName(); // 解决普通输入项的数据的中文乱码问题 String value = item.getString("UTF-8"); // value = new String(value.getBytes("GB2312"),"UTF-8"); System.out.println(name + "=" + value); } else {// 如果fileitem中封装的是上传文件 // 得到上传的文件名称, String filename = item.getName(); System.out.println(filename); if (filename == null || filename.trim().equals("")) { continue; } // 注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: // c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt // 处理获取到的上传文件的文件名的路径部分,只保留文件名部分 filename = filename.substring(filename.lastIndexOf("\\") + 1); // 获取item中的上传文件的输入流 InputStream in = item.getInputStream(); // 创建一个文件输出流 FileOutputStream out = new FileOutputStream(savePath + "\\" + filename); // 创建一个缓冲区 byte buffer[] = new byte[1024]; // 判断输入流中的数据是否已经读完的标识 int len = 0; // 循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据 while ((len = in.read(buffer)) > 0) { // 使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" // + filename)当中 out.write(buffer, 0, len); } // 关闭输入流 in.close(); // 关闭输出流 out.close(); // 删除处理文件上传时生成的临时文件 item.delete(); message = "文件上传成功!"; } } } catch (Exception e) { message = "文件上传失败!"; e.printStackTrace(); } request.setAttribute("message", message); request.getRequestDispatcher("/message.jsp").forward(request, response); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub RequestContext req = new ServletRequestContext(request); if (FileUpload.isMultipartContent(req)) { DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload fileUpload = new ServletFileUpload(factory); fileUpload.setHeaderEncoding("UTF-8"); fileUpload.setFileSizeMax(1024 * 1024 * 1024); List items = new ArrayList(); try { items = fileUpload.parseRequest(request); } catch (Exception e) { } Iterator it = items.iterator(); while (it.hasNext()) { FileItem fileItem = (FileItem) it.next(); if (fileItem.isFormField()) { System.out.println(fileItem.getFieldName() + " " + fileItem.getName() + " " + new String(fileItem.getString().getBytes("UTF-8"), "UTF-8")); } else { System.out.println(fileItem.getFieldName() + " " + fileItem.getName() + " " + fileItem.isInMemory() + " " + fileItem.getContentType() + " " + fileItem.getSize()); if (fileItem.getName() != null && fileItem.getSize() != 0) { File fullFile = new File(fileItem.getName()); File newFile = new File("D:\\" + fullFile.getName()); try { fileItem.write(newFile); } catch (Exception E) { } response.getWriter().write("Success!"); } else { System.out.println("no file choosen or empty file"); response.getWriter().print("Failure!"); } } } } } }
注:客户端发送文件上传请求后会调用doPost方法,上传后的文件会保存在D盘,上传成功后会返回结果给客户端。doPost部分代码的解释可参照doGet