构建multipart/form-data实现文件上传

通常文件上传都是通过form表单中的file控件,并将form中的content-type设置为multipart/form-data。现在我们通过java来构建这部分请求内容实现文件上传功能。

一、关于multipart/form-data

文件上传本质上是一个POST请求。只不过请求头以及请求内容遵循一定的规则(协议)

  • 请求头(Request Headers)中需要设置 Content-Type 为 multipart/form-data; boundary=${boundary}。其中${boundary}分割线,需要在代码中替换,且尽量复杂,不易重复

  • 请求正文(Request Body)需要使用在 Header中设置的 ${boundary}来分割当前正文中的FormItem,内容格式如下

    --${boundary}
    Content-Disposition: form-data; name="id" testCodeUpload
    --${boundary}
    Content-Disposition: form-data; name="file";filename="xx.txt"
    Content-Type: application/octet-stream {{这里写入文件流}}
    --${boundary}--

    正文开始以前缀+${boundary}开始,以 前缀 +${boundary}+前缀结束。中间每个FormItem 以 前缀+${boundary}开始,以一个空白的换行结束。

二、代码实现

实例代码采用HttpURLConnection实现一个简单POST请求

  • 建立http请求,设置基本参数

URL postUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) postUrl.openConnection();
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("Charset", "UTF-8");
  • 添加文件上传必须的请求信息,获取http请输出流

String boundary = "----" + UUID.randomUUID().toString();
conn.setRequestProperty("Content-Type",
"multipart/form-data; boundary=" + boundary);
OutputStream out = conn.getOutputStream();
StringBuilder sb = new StringBuilder();
  • 一组FormItem

sb.append(boundaryPrefix);
sb.append(boundary);
sb.append(newLine);
sb.append("Content-Disposition: form-data; name=\"id\"");
sb.append(newLine);
sb.append(newLine);
sb.append("testCodeUpload");
sb.append(newLine);
  • 文件写人

sb.append(boundaryPrefix);
sb.append(boundary);
sb.append(newLine);
sb.append("Content-Disposition: form-data; name=\"file\"; filename=\""
+ fileName + "\"");
sb.append("Content-Type: application/octet-stream");
sb.append(newLine);
sb.append(newLine);
out.write(sb.toString().getBytes());
File file = new File(file1);
FileInputStream in = new FileInputStream(file);
byte[] bufferOut = new byte[1024];
int bytes = 0;
while ((bytes = in.read(bufferOut)) != -1) {
out.write(bufferOut, 0, bytes);
}
out.write(newLine.getBytes());
in.close();
  • 结束标志 前缀+boundary +前缀
byte[] end_data = (newLine + boundaryPrefix + boundary + boundaryPrefix + newLine)
.getBytes();
out.write(end_data);
out.flush();
out.close();

三、文件接收

  • 文件接收端通过迭代每个FileItem获取不同的数据

FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
try {
items = upload.parseRequest(request);
} catch (FileUploadException ex) {
ex.printStackTrace();
out.println(ex.getMessage());
return;
}
Iterator<FileItem> itr = items.iterator();
String id = "", fileName = "";
int chunks = 1, chunk = 0;
FileItem tempFileItem = null;
while (itr.hasNext()) {
FileItem item = (FileItem) itr.next();
if (item.getFieldName().equals("id")) {
id = item.getString();
} else if (item.getFieldName().equals("name")) {
fileName = new String(item.getString().getBytes("ISO-8859-1"), "UTF-8");
} else if (item.getFieldName().equals("file")) {
tempFileItem = item;
}

四、总结

通过代码实现一遍文件上传,了解其运行机制,解开了以前在写文件上传代码中item.getFieldName().equals("name")等相关判断的疑惑。所以,对于已有的基础代码,还是多看,多写,多实践。

附完整代码

 @Test
public void buildUploadStream() throws IOException {
String url ="uploadurl";
file1 = "D:\\test.xls";
fileName = "test.xls"; String newLine = "\r\n";
String boundaryPrefix = "--";
String boundary = "----" + UUID.randomUUID().toString(); URL postUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) postUrl.openConnection(); conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false); conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty("Content-Type",
"multipart/form-data; boundary=" + boundary); OutputStream out = conn.getOutputStream(); StringBuilder sb = new StringBuilder(); sb.append(boundaryPrefix);
sb.append(boundary);
sb.append(newLine); sb.append("Content-Disposition: form-data; name=\"id\"");
sb.append(newLine);
sb.append(newLine);
sb.append("testCodeUpload");
sb.append(newLine); sb.append(boundaryPrefix);
sb.append(boundary);
sb.append(newLine); sb.append("Content-Disposition: form-data; name=\"name\"");
sb.append(newLine);
sb.append(newLine);
sb.append(fileName);
sb.append(newLine); sb.append(boundaryPrefix);
sb.append(boundary);
sb.append(newLine); sb.append("Content-Disposition: form-data; name=\"file\"; filename=\""
+ fileName + "\"");
sb.append("Content-Type: application/octet-stream");
sb.append(newLine);
sb.append(newLine); out.write(sb.toString().getBytes()); File file = new File(file1);
FileInputStream in = new FileInputStream(file);
byte[] bufferOut = new byte[1024];
int bytes = 0;
while ((bytes = in.read(bufferOut)) != -1) {
out.write(bufferOut, 0, bytes);
}
out.write(newLine.getBytes());
in.close();
byte[] end_data = (newLine + boundaryPrefix + boundary + boundaryPrefix + newLine)
.getBytes();
out.write(end_data);
out.flush();
out.close(); BufferedReader reader = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}

all code

最新文章

  1. GbkToUtf8 Utf8ToGbk PackHttp
  2. [WebServer] Windows操作系统下 Tomcat 服务器运行 PHP 的环境配置
  3. phpcms后台获取当前登录账号的数据
  4. [转]Python学习资料和教程pdf
  5. SQLite 批量insert - 如何加速SQLite的插入操作
  6. Vim优化
  7. Java反射 - 3(动态代理)
  8. python----脚本文件的头部写法。
  9. ORACLE - 管理重做日志文件
  10. [补档][Poi2014]FarmCraft
  11. 毕业回馈-89c51之定时器/计数器(Timer/Count)
  12. 关于angular 的路由title设置
  13. LR 场景选项配置--笔记
  14. BZOJ3724PA2014Final Krolestwo——欧拉回路+构造
  15. lambda表达式,filter,map,reduce,curry,打包与解包和
  16. var 全局变量 局部变量
  17. Nginx挂载维护页或返回自定义响应信息
  18. Bootstrap 4正式发布还有意义吗?
  19. 关于SpringKafka消费者的几个监听器:[一次处理单条消息和一次处理一批消息]以及[自动提交offset和手动提交offset]
  20. windows server 2008 r2 IIS 6 元数据库与IIS 6 配置的兼容性 解决方案

热门文章

  1. Docker中mysql大小写敏感配置不起作用的问题排查
  2. 从Uber微服务看最佳实践如何炼成?
  3. FbinstTools制作多系统启动U盘(Windows+Linux)
  4. c/c++再学习:排序算法了解
  5. day20.序列化模块
  6. DAY1 VS2017&amp;CUDA10.01环境搭建
  7. 业务线B/C端业务组件总结
  8. Random Erasing Augmentation(REA)
  9. 蓝桥杯刷题,第四界省赛B组
  10. Falsy Bouncer 过滤数组假值