一、概述

  需求:通过html+js+java上传最大500M的文件,需要做MD5 消息摘要以及SHA256签名,文件上传至云存储

1.1、理解http协议

  https://www.cnblogs.com/bjlhx/category/1198166.html

  http传输的都是二进制数据,可以看成传输的都是字符。

  http协议其实就是对socket接受到的数据进行解析,或者将按照http协议的格式把数据写到socket中

  HTTP文件上传是做Web开发时的常见功能,例如上传图片、上传影片等。实现HTTP文件上传也比较简单,用任何Web端的脚本都可以轻松实现,例如PHP、JSP都有现成的函数或者类来调用。

  经过分析后发现,原来PHP、JAVA的上传是先由服务器缓存为临时文件,或者服务器将上传数据缓存到内存中后,再由脚本调用相关的上传文件处理函数来移动临时文件来保存文件数据;由于PHP、JAVA等处理文件上传需要分两步,对于大文件与超大文件来说, 再次移动文件也是比较耗时间与系统资源的,由于浏览器将文件提交到服务器上后就会等待服务器端的响应,服务器端移动文件耗时太长,导致浏览器等待超时而报错。

1.2、HTTP文件上传的技术原理

  HTTP文件上传是通过  multipart/form-data 协议实现的,multipart/form-data实际上是一种数据的编码分割方式,例如在浏览器端编写一个文件上传的页面,向服务器发送POST请求后,服务器端将会收到数据。

  multipart/form-data需要首先在HTTP请求头设置一个分隔符,例如:WebKitFormBoundarydCC44akR5BzKXSP1:参看请求头数据

  然后,将每个字段用“--分隔符”分隔,最后一个“--分隔符--”表示结束。

  例如,要上传一个name字段"Today"和一个文件11.gif,HTTP正文可以通过Chrome浏览器开发者工具查看【F12】,目前我使用的没有展示Request  Payload ,可以使用wireshark抓包查看

1.2.1、wireshark 配置抓包:tcp.port eq 8080

打开网站

  

点击上传文件按钮

  

  

分析

  1、三次握手建立tcp链接:57行,客户端发送syc,58行服务端回复syc和ack,59行客户端回复ack,其中60行  TCP Window Update:滑动窗口为0后,发送方停止发送数据,如果接收方滑动窗口出现空闲空间,则接收方主动发送TCP Window Update来更新发送方的滑动窗口。

  2、数据传输:61行,Push+ACK包:数据包协议+ACK包,这样是为了减少网络流量;62行,服务器端返回ack,63行,具体数据传输,以及ack,后续就是没三行一次的循环上传数据   

1.2.2、配置查看 TCP 流

  

查看整个请求响应过程【文件流删除大部分内容】

POST /manage/uploadFile HTTP/1.1
Host: zs.jd.com:8080
Connection: keep-alive
Content-Length: 335334
Cache-Control: max-age=0
Origin: http://zs.jd.com:8080
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywcj3RACSzuBGHt5g
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://zs.jd.com:8080/file.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 ------WebKitFormBoundarywcj3RACSzuBGHt5g
Content-Disposition: form-data; name="name" sss
------WebKitFormBoundarywcj3RACSzuBGHt5g
Content-Disposition: form-data; name="file"; filename="11.gif"
Content-Type: image/gif GIF89a+...w..!..NETSCAPE2.0.......=;.@.;
------WebKitFormBoundarywcj3RACSzuBGHt5g-- HTTP/1.1 200
Transfer-Encoding: chunked
Date: Thu, 06 Jun 2019 02:24:05 GMT 0 GET /favicon.ico HTTP/1.1
Host: zs.jd.com:8080
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://zs.jd.com:8080/manage/uploadFile
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 HTTP/1.1 200
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 0
Date: Thu, 06 Jun 2019 02:24:05 GMT

二、代码开发

2.1、jar配置

POM jar

        <dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>

因为使用springboot,配置基础参数

spring.http.multipart.max-file-size=500MB
spring.http.multipart.max-request-size=500MB

代码类:只要是Spring 生态的应用程序,文件的接收都是使用MutipartFile这个类型,它表示通过 mutipart 请求上传了的一个文件。如果多个文件上传,那就用数组,如 MutipartFile[] 。

2.2、基本上传

html

<form enctype="multipart/form-data"
action="/bs/test/uploadFile/cloud" method="post"> 姓名:<input type="text" name="name">
上传文件: <input type="file" name="file" />
<br/>
<input type="submit" value="上传"/>
</form>

java代码

util方法

import java.io.*;
import java.security.MessageDigest; import org.apache.commons.codec.binary.Hex; public class CommonHelper { public static String msgSafeBase(String msg, String algorithmName) throws Exception {
return msgSafeBase(msg.getBytes("UTF8"), algorithmName);
} public static String msgSafeBase(byte[] data, String algorithmName) throws Exception {
MessageDigest m = MessageDigest.getInstance(algorithmName);
m.update(data);
byte s[] = m.digest();
return Hex.encodeHexString(s);
} public static String msgSafeBase(InputStream inputStream, String algorithmName) throws Exception {
MessageDigest m = MessageDigest.getInstance(algorithmName); //分多次将一个文件读入,对于大型文件而言,比较推荐这种方式,占用内存比较少。
byte[] buffer = new byte[1024];
int length = -1;
while ((length = inputStream.read(buffer, 0, 1024)) != -1) {
m.update(buffer, 0, length);
}
inputStream.close(); byte s[] = m.digest();
return Hex.encodeHexString(s);
} public static String msgSafeBaseMD5(byte[] data) throws Exception {
return msgSafeBase(data, "MD5");
} public static String msgSafeBaseMD5(InputStream inputStream) throws Exception {
return msgSafeBase(inputStream, "MD5");
} public static String msgSafeBaseSHA256(byte[] data) throws Exception {
return msgSafeBase(data, "SHA-256");
} public static String msgSafeBaseSHA256(InputStream inputStream) throws Exception {
return msgSafeBase(inputStream, "SHA-256");
}
}

接收方法

    @RequestMapping("/uploadFile/cloud2")
public Object bigFile(HttpServletRequest request, HttpServletResponse response, String guid, String md5,
Integer chunk, @RequestParam(required = false, value = "file") MultipartFile file, Integer chunks) {
    String baseMD5 = CommonHelper.msgSafeBaseMD5(file.getBytes());
    String sha256 = CommonHelper.msgSafeBaseSHA256(file.getBytes());
  fIleResp.getData().setMd5(baseMD5);
  fIleResp.getData().setSha256(sha256);   String s = fileService.upLoadFileStream(fileProperty, file.getInputStream(), file.getSize());
}

2.3、使用前端webuploader 不分片处理

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
<title>Title</title>
<link href="https://cdn.staticfile.org/webuploader/0.1.5/webuploader.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="http://cdn.staticfile.org/jquery/1.10.2/jquery.js"></script>
<script type="text/javascript" src="http://cdn.staticfile.org/webuploader/0.1.5/webuploader.min.js"></script>
</head>
<body>
<div id="uploader" class="wu-example">
<!--用来存放文件信息-->
<div id="thelist" class="uploader-list"></div>
<div class="btns">
<div id="picker">选择文件</div>
<button id="ctlBtn" class="btn btn-default" style="display: none">开始上传</button>
</div> <p>目前的进度如下:</p>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
<span class="sr-only">60% Complete</span>
</div>
</div>
</div>
</body> <script type="text/javascript">
var $ = jQuery,
$list = $('#thelist'),
state = 'pending',
$btn = $('#ctlBtn'); var GUID = WebUploader.Base.guid();//一个GUID
var uploader = WebUploader.create({
// swf文件路径
swf: 'http://cdn.staticfile.org/webuploader/0.1.5/Uploader.swf',
// 文件接收服务端。
server: './bs/test/uploadFile/cloud',
formData: {
guid: GUID,
md5: ''
},
// 选择文件的按钮。可选。
// 内部根据当前运行是创建,可能是input元素,也可能是flash.
pick: '#picker',
chunked: false, // 分片处理
chunkSize: 500 * 1024 * 1024, // 每片500M,
chunkRetry: false,// 如果失败,则不重试
threads: 5,// 上传并发数。允许同时最大上传进程数。
// 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传! auto: false,
resize: false
});
$("#ctlBtn").click(function () {
uploader.upload();
});
//当文件上传成功时触发。
uploader.on("uploadSuccess", function (file) {
console.log(file) $('#' + file.id).find('p.state').append('已经上传');
alert('上传成功!');
}); // 文件上传过程中创建进度条实时显示。
uploader.on('uploadProgress', function (file, percentage) {
var $li = $('#' + file.id),
$percent = $li.find('.progress .progress-bar'); // 避免重复创建
if (!$percent.length) {
$percent = $('<div class="progress progress-striped active">' +
'<div class="progress-bar" role="progressbar" style="width: 0%">' +
'</div>' +
'</div>').appendTo($li).find('.progress-bar');
} // $li.find('p.state').append('开始上传……'); $percent.css('width', percentage * 100 + '%');
}); // 当有文件被添加进队列的时候
uploader.on('fileQueued', function (file) {
console.log("文件队列事件被触发..");
$list.append('<div id="' + file.id + '" class="item">' +
'<h4 class="info">' + file.name + '</h4>' +
'<p class="state"></p>' +
'</div>');
var _file = $("#" + file.id);
//计算md5
uploader.md5File(file)
// 及时显示进度
.progress(function (percentage) {
//console.log('Percentage:', percentage);
_file.find("p").html("准备中:" + percentage * 100 + "%");
})
// 完成
.then(function (val) {
uploader.options.formData.md5 = val;
_file.find("p").append("md5:" + val);
$btn.show();
});
});
</script>
</html>

三、技术点说明

3.1、spring 的 MultipartFile 说明

    String getName();

    String getOriginalFilename();

    String getContentType();

    boolean isEmpty();

    long getSize();

    byte[] getBytes() throws IOException;

    InputStream getInputStream() throws IOException;

    void transferTo(File var1) throws IOException, IllegalStateException;

其实这里面已经包含了 输入流、文件的字节数组

文件的md5、sha256可以使用上述的文件字节数组

3.2、文件输入流转二进制数据、二进制转输出流文件

//将文件写入程序(以字节数组的形式);
public static byte[] FiletoByte(String path){
File myFile=new File(path);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
FileInputStream myInputStream=new FileInputStream(myFile);
int len=-1;
byte[]car=new byte[1024*10];
while((len=myInputStream.read(car))!=-1)
{
bos.write(car, 0, len);
bos.flush();
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return bos.toByteArray(); } //将字节写入文件(以字节数组的形式);
public static void BytetoFile(String path,byte[]datas)
{
System.out.println(datas.length);
File myFile=new File(path);
ByteArrayInputStream bis = new ByteArrayInputStream(datas);
try {
int len;
byte[]car=new byte[1024*10];
FileOutputStream fileOutputStream=new FileOutputStream(myFile);
while((len=bis.read(car))!=-1)
{
fileOutputStream.write(car, 0, len);
fileOutputStream.flush();
} } catch (Exception e) {
e.printStackTrace();
}finally {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

3.5、容器配置

  如果使用tomcat 注意配置:004-tomcat优化-Catalina中JVM优化、Connector优化、NIO化

  nginx配置,在location上配置:client_max_body_size 500m;

发送到

005-html+js+spring multipart文件上传的更多相关文章

  1. spring实现文件上传(图片解析)

    合抱之木,生于毫末,千里之行,始于足下,要想了解spring的文件上传功能,首先要知道spring是通过流的方式将文件进行解析,然后上传.那么是不是所有需要用的文件上传的地方都要写一遍文件解析器呢? ...

  2. springboot成神之——spring的文件上传

    本文介绍spring的文件上传 目录结构 配置application DemoApplication WebConfig TestController 前端上传 本文介绍spring的文件上传 目录结 ...

  3. Spring MVC 笔记 —— Spring MVC 文件上传

    文件上传 配置MultipartResolver <bean id="multipartResolver" class="org.springframework.w ...

  4. Spring Boot 文件上传原理

    首先我们要知道什么是Spring Boot,这里简单说一下,Spring Boot可以看作是一个框架中的框架--->集成了各种框架,像security.jpa.data.cloud等等,它无须关 ...

  5. Spring MVC文件上传教程 commons-io/commons-uploadfile

    Spring MVC文件上传教程 commons-io/commons-uploadfile 用到的依赖jar包: commons-fileupload 1.3.1 commons-io 2.4 基于 ...

  6. spring boot文件上传、下载

    主题:Spring boot 文件上传(多文件上传)[从零开始学Spring Boot]http://www.iteye.com/topic/1143595 Spring MVC实现文件下载http: ...

  7. 【Java Web开发学习】Spring MVC文件上传

    [Java Web开发学习]Spring MVC文件上传 转载:https://www.cnblogs.com/yangchongxing/p/9290489.html 文件上传有两种实现方式,都比较 ...

  8. Spring mvc文件上传实现

    Spring mvc文件上传实现 jsp页面客户端表单编写 三个要素: 1.表单项type="file" 2.表单的提交方式:post 3.表单的enctype属性是多部分表单形式 ...

  9. JavaScript进阶(九)JS实现本地文件上传至阿里云服务器

    JS实现本地文件上传至阿里云服务器 前言 在前面的博客< JavaScript进阶(八)JS实现图片预览并导入服务器功能>(点击查看详情)中,实现了JS将本地图片文件预览并上传至阿里云服务 ...

随机推荐

  1. Image Processing and Analysis_8_Edge Detection:Local Scale Control for Edge Detection and Blur Estimation——1998

    此主要讨论图像处理与分析.虽然计算机视觉部分的有些内容比如特 征提取等也可以归结到图像分析中来,但鉴于它们与计算机视觉的紧密联系,以 及它们的出处,没有把它们纳入到图像处理与分析中来.同样,这里面也有 ...

  2. 深度学习之卷积神经网络CNN及tensorflow代码实例

    深度学习之卷积神经网络CNN及tensorflow代码实例 什么是卷积? 卷积的定义 从数学上讲,卷积就是一种运算,是我们学习高等数学之后,新接触的一种运算,因为涉及到积分.级数,所以看起来觉得很复杂 ...

  3. Flutter中的替换路由、返回到根路由

    替换路由 当我们有三个页面,页面1,页面2,页面3. 期望点击页面1按钮,跳转到页面2,页面2点击返回,跳转到页面1: 点击页面2按钮,跳转到页面3,页面3点击返回,跳转到页面1,而不是页面2. 这时 ...

  4. github安全整理

    漏洞及渗透练习平台: WebGoat漏洞练习平台: https://github.com/WebGoat/WebGoat webgoat-legacy漏洞练习平台: https://github.co ...

  5. 【2017-06-29】在登录页面自动返回上次请求页面、Js获取table中的行数与列数

    一.在登录页面自动返回上次请求页面 Request.UrlReferrer比如 if (Request.UrlReferrer != null) { //如果能获取来路地址 Response.Redi ...

  6. 05.网站点击流数据分析项目_模块开发_ETL

    项目的数据分析过程在hadoop集群上实现,主要应用hive数据仓库工具,因此,采集并经过预处理后的数据,需 要加载到hive数据仓库中,以进行后续的挖掘分析. ETL:用来描述将数据从来源端经过抽取 ...

  7. mongodb模式模型设计及编码-Mongoose

    走到这一步,我们的网站还不能称为动态的网站,因为所要的数据都是伪造的,所以现在要对数据库的模型进行设计   Mongoose 我们用到的工具模块是Mongoose,他能对Mongodb进行建模的这样一 ...

  8. 解决CentOS虚拟机开机黑屏卡死问题

    默认配置 导致的错误 1.直接就是黑屏,连杠杠都没有 2.centos系统关不掉 3.关闭vmware提示:虚拟机XXX繁忙 解决方式 一(我的失败,依旧不行)1.强制关闭vmware,重启计算机(不 ...

  9. Dapper+Mysql 使用LIKE模糊查询写法踩坑

    LIKE '%@Title%' 会解析成'%'@Title'%' 这里用拼接也是不行的'%'+@Title+'%' 只能用MySQL函数方法拼接 public dynamic GetListByFil ...

  10. appium+python 【Mac】UI自动化测试封装框架流程简介 <一>

    为了多人之间更方便的协作,那么框架本身的结构和编写方式将变得很重要,因此每个团队都有适合自己的框架.如下本人对APP的UI自动化测试的框架进行进行了简单的汇总.主要目的是为了让团队中的其余人员接手写脚 ...