需求:经过html+js+java上传最大500M的文件,须要作MD5 消息摘要以及SHA256签名,文件上传至云存储javascript
https://www.cnblogs.com/bjlhx/category/1198166.htmlcss
http传输的都是二进制数据,能够当作传输的都是字符。html
http协议其实就是对socket接受到的数据进行解析,或者将按照http协议的格式把数据写到socket中前端
HTTP文件上传是作Web开发时的常见功能,例如上传图片、上传影片等。实现HTTP文件上传也比较简单,用任何Web端的脚本均可以轻松实现,例如PHP、JSP都有现成的函数或者类来调用。java
通过分析后发现,原来PHP、JAVA的上传是先由服务器缓存为临时文件,或者服务器将上传数据缓存到内存中后,再由脚本调用相关的上传文件处理函数来移动临时文件来保存文件数据;因为PHP、JAVA等处理文件上传须要分两步,对于大文件与超大文件来讲, 再次移动文件也是比较耗时间与系统资源的,因为浏览器将文件提交到服务器上后就会等待服务器端的响应,服务器端移动文件耗时太长,致使浏览器等待超时而报错。jquery
HTTP文件上传是经过 multipart/form-data 协议实现的,multipart/form-data其实是一种数据的编码分割方式,例如在浏览器端编写一个文件上传的页面,向服务器发送POST请求后,服务器端将会收到数据。nginx
multipart/form-data须要首先在HTTP请求头设置一个分隔符,例如:WebKitFormBoundarydCC44akR5BzKXSP1:参看请求头数据web
而后,将每一个字段用“--分隔符”分隔,最后一个“--分隔符--”表示结束。spring
例如,要上传一个name字段"Today"和一个文件11.gif,HTTP正文能够经过Chrome浏览器开发者工具查看【F12】,目前我使用的没有展现Request Payload ,可使用wireshark抓包查看apache
打开网站
点击上传文件按钮
分析
一、三次握手创建tcp连接:57行,客户端发送syc,58行服务端回复syc和ack,59行客户端回复ack,其中60行 TCP Window Update:滑动窗口为0后,发送方中止发送数据,若是接收方滑动窗口出现空闲空间,则接收方主动发送TCP Window Update来更新发送方的滑动窗口。
二、数据传输:61行,Push+ACK包:数据包协议+ACK包,这样是为了减小网络流量;62行,服务器端返回ack,63行,具体数据传输,以及ack,后续就是没三行一次的循环上传数据
查看整个请求响应过程【文件流删除大部份内容】
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
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[] 。
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());
}
<!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>
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;
其实这里面已经包含了 输入流、文件的字节数组
文件的md五、sha256可使用上述的文件字节数组
//将文件写入程序(以字节数组的形式); 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.五、容器配置
若是使用tomcat 注意配置:004-tomcat优化-Catalina中JVM优化、Connector优化、NIO化
nginx配置,在location上配置:client_max_body_size 500m;
发送到