1、事先了解
1.1
请求格式
我们使用http来上传文件,必须先了解http的请求格式,然后才好发报。主要分为以下四个部分:
(1)分界符:由两个连字符“--”和任意字符串组成;
(2)标准http报文格式,来形容上传文件的相关信息,包括请求参数名,上传文件名,文件类型,接收语言等。
(3)上传文件的内容,通常是字节流的形式;
(4)文件全部上传后的结束符:与分界符类似,只不过需要在分界符后面再加两个连字符。
1.2 http报文格式
(1)http请求报文
一个http请求报文格式是由四个部分组成,分别是请求行,请求头部,空行和请求数据。其中需要注意的是空行
是必须的。
- 请求行
请求行是由请求方法、URL字段和HTTP协议版本这个3个部分组成。
请求方法有8个——
GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT。
一般常用的就是GET
和 POST
方法。
简单说下两者区别——
(1)GET
绝大部分的HTTP请求报文使用的是GET方法,如get这个单词本身含义一样,GET方法是用于获取查询资源信息,当浏览器请求一个对象时,使用GET方法。
(2)POST方法
HTTP客户机常常在用户提交表单时使用POST方法,需要用到实体主体。POST表示可能修改变服务器上的资源的请求。
get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet,
post与get的不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内。
URL字段就是"http://192.168.1.110:8080/abc"这种浏览器访问地址。
HTTP协议版本就是1.0
或者1.1
这两种其中之一。
- 请求头部
2、Android端
上传代码
httpUrlConnection是 URLConnection的子类,我们先来看看
URLConnection这个抽象类的相关介绍。注:以下内容来自 JDK API 1.6
抽象类URLConnection
是所有类的超类,它代表应用程序和 URL
之间的通信链接。此类的实例可用于读取和写入此 URL
引用的资源。通常,创建一个到 URL
的连接需要几个步骤:
openConnection() |
connect() |
对影响到远程资源连接的参数进行操作。 |
与资源交互;查询头字段和内容。 |
---------------------------->
时间
- 通过在 URL 上调用 openConnection
方法创建连接对象。
- 处理设置参数和一般请求属性。
- 使用 connect
方法建立到远程对象的实际连接。
- 远程对象变为可用。远程对象的头字段和内容变为可访问。
使用以下方法修改设置参数:
- setAllowUserInteraction
- setDoInput
- setDoOutput
- setIfModifiedSince
- setUseCaches
使用以下方法修改一般请求属性:
- setRequestProperty
使用setDefaultAllowUserInteraction
和 setDefaultUseCaches
可设置 AllowUserInteraction
和 UseCaches参数的默认值。
上面每个set
方法都有一个用于获取参数值或一般请求属性值的对应 get
方法。适用的具体参数和一般请求属性取决于协议。
在建立到远程对象的连接后,以下方法用于访问头字段和内容:
- getContent
- getHeaderField
- getInputStream
- getOutputStream
某些头字段需要经常访问。以下方法:
- getContentEncoding
- getContentLength
- getContentType
- getDate
- getExpiration
- getLastModifed
提供对这些字段的便捷访问。getContent方法使用 getContentType
方法以确定远程对象类型;子类重写 getContentType
方法很容易。
一般情况下,所有的预连接参数和一般请求属性都可忽略:预连接参数和一般请求属性默认为敏感值。对于此接口的大多数客户端而言,只有两个需要的方法:getInputStream和getContent,它们通过便捷方法镜像到
URL 类中。
而API上对 HttpURLConnection
的介绍是:
每个HttpURLConnection 实例都可用于生成单个请求,但是其他实例可以透明地共享连接到 HTTP 服务器的基础网络。请求后在HttpURLConnection
的 InputStream
或 OutputStream
上调用 close()
方法可以释放与此实例关联的网络资源,但对共享的持久连接没有任何影响。如果在调用 disconnect()
时持久连接空闲,则可能关闭基础套接字。
因此,由上可知,HttpURLConnection是java用于特定的HTTP通信的类。通过使用HttpURLConnection对象的方法设置相关参数,我们就能进行http通信。
(1)public
void setDoInput(boolean doinput)
方法
如果打算使用 URL 连接进行输入,则将 DoInput 标志设置为 true;如果不打算使用,则设置为 false。默认值为true。
(2)public
void setDoOutput(boolean dooutput)
将此 URLConnection
的 doOutput
字段的值设置为指定的值。
URL 连接可用于输入和/或输出。如果打算使用 URL 连接进行输出,则将 DoOutput标志设置为 true;如果不打算使用,则设置为 false。默认值为 false。
(3)public
void setUseCaches(boolean usecaches)
将此 URLConnection
的 useCaches
字段的值设置为指定的值。
有些协议用于文档缓存。有时候能够进行“直通”并忽略缓存尤其重要,例如浏览器中的“重新加载”按钮。如果连接中的UseCaches 标志为 true,则允许连接使用任何可用的缓存。如果为
false,则忽略缓存。默认值来自 DefaultUseCaches,它默认为 true。
具体代码如下:
/* 上传文件至Server的方法 */ private void uploadFile() { String end = "\r\n"; String twoHyphens = "--"; String boundary = "*****"; String newName = "image.jpg"; String uploadFile = "storage/sdcard1/bagPictures/102.jpg"; ; String actionUrl = "http://192.168.1.123:8080/upload/servlet/UploadServlet"; try { URL url = new URL(actionUrl); HttpURLConnection con = (HttpURLConnection) url.openConnection(); /* 允许Input、Output,不使用Cache */ con.setDoInput(true); con.setDoOutput(true); con.setUseCaches(false); /* 设置传送的method=POST */ con.setRequestMethod("POST"); /* setRequestProperty */ con.setRequestProperty("Connection", "Keep-Alive"); con.setRequestProperty("Charset", "UTF-8"); con.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); /* 设置DataOutputStream */ DataOutputStream ds = new DataOutputStream(con.getOutputStream()); ds.writeBytes(twoHyphens + boundary + end); ds.writeBytes("Content-Disposition: form-data; " + "name=\"file1\";filename=\"" + newName + "\"" + end); ds.writeBytes(end); /* 取得文件的FileInputStream */ FileInputStream fStream = new FileInputStream(uploadFile); /* 设置每次写入1024bytes */ int bufferSize = 1024; byte[] buffer = new byte[bufferSize]; int length = -1; /* 从文件读取数据至缓冲区 */ while ((length = fStream.read(buffer)) != -1) { /* 将资料写入DataOutputStream中 */ ds.write(buffer, 0, length); } ds.writeBytes(end); ds.writeBytes(twoHyphens + boundary + twoHyphens + end); /* close streams */ fStream.close(); ds.flush(); /* 取得Response内容 */ InputStream is = con.getInputStream(); int ch; StringBuffer b = new StringBuffer(); while ((ch = is.read()) != -1) { b.append((char) ch); } /* 将Response显示于Dialog */ showDialog("上传成功" + b.toString().trim()); /* 关闭DataOutputStream */ ds.close(); } catch (Exception e) { showDialog("上传失败" + e); } }
3、服务器端的代码
public class UploadServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { System.out.println("IP:" + request.getRemoteAddr()); // 1、创建工厂类:DiskFileItemFactory DiskFileItemFactory facotry = new DiskFileItemFactory(); String tempDir = getServletContext().getRealPath("/WEB-INF/temp"); facotry.setRepository(new File(tempDir));//设置临时文件存放目录 // 2、创建核心解析类:ServletFileUpload ServletFileUpload upload = new ServletFileUpload(facotry); upload.setHeaderEncoding("UTF-8");// 解决上传的文件名乱码 upload.setFileSizeMax(1024 * 1024 * 1024);// 单个文件上传最大值是1M upload.setSizeMax(2048 * 1024 * 1024);//文件上传的总大小限制 // 3、判断用户的表单提交方式是不是multipart/form-data boolean bb = upload.isMultipartContent(request); if (!bb) { return; } // 4、是:解析request对象的正文内容List<FileItem> List<FileItem> items = upload.parseRequest(request); String storePath = getServletContext().getRealPath( "/WEB-INF/upload");// 上传的文件的存放目录 for (FileItem item : items) { if (item.isFormField()) { // 5、判断是否是普通表单:打印看看 String fieldName = item.getFieldName();// 请求参数名 String fieldValue = item.getString("UTF-8");// 请求参数值 System.out.println(fieldName + "=" + fieldValue); } else { // 6、上传表单:得到输入流,处理上传:保存到服务器的某个目录中,保存时的文件名是啥? String fileName = item.getName();// 得到上传文件的名称 C:\Documents // and // Settings\shc\桌面\a.txt // a.txt //解决用户没有选择文件上传的情况 if(fileName==null||fileName.trim().equals("")){ continue; } fileName = fileName .substring(fileName.lastIndexOf("\\") + 1); String newFileName = UUIDUtil.getUUID() + "_" + fileName; System.out.println("上传的文件名是:" + fileName); InputStream in = item.getInputStream(); String savePath = makeDir(storePath, fileName) + "\\" + newFileName; OutputStream out = new FileOutputStream(savePath); byte b[] = new byte[1024]; int len = -1; while ((len = in.read(b)) != -1) { out.write(b, 0, len); } in.close(); out.close(); item.delete();//删除临时文件 } } }catch(FileUploadBase.FileSizeLimitExceededException e){ request.setAttribute("message", "单个文件大小不能超出5M"); request.getRequestDispatcher("/message.jsp").forward(request, response); }catch(FileUploadBase.SizeLimitExceededException e){ request.setAttribute("message", "总文件大小不能超出7M"); request.getRequestDispatcher("/message.jsp").forward(request, response); }catch (Exception e) { e.printStackTrace(); request.setAttribute("message", "上传失败"); request.getRequestDispatcher("/message.jsp").forward(request, response); } } // WEB-INF/upload/1/3 打散存储目录 private String makeDir(String storePath, String fileName) { int hashCode = fileName.hashCode();// 得到文件名的hashcode码 int dir1 = hashCode & 0xf;// 取hashCode的低4位 0~15 int dir2 = (hashCode & 0xf0) >> 4;// 取hashCode的高4位 0~15 String path = storePath + "\\" + dir1 + "\\" + dir2; File file = new File(path); if (!file.exists()) file.mkdirs(); System.out.println("存储路径是"+path); return path; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
4、实验结果
用httpUrlConnection实现文件上传,布布扣,bubuko.com