HTML5实现浏览器端大文件分片上传
2021-03-31 22:24
标签:指示 w3c 空格 tostring false bug 搭建 parse tab 我们平时经常做的是上传文件,上传文件夹与上传文件类似,但也有一些不同之处,这次做了上传文件夹就记录下以备后用。 这次项目的需求: 支持大文件的上传和续传,要求续传支持所有浏览器,包括ie6,ie7,ie8,ie9,Chrome,Firefox,360安全浏览器,并且刷新浏览器后仍然能够续传,重启浏览器(关闭浏览器后再打开)仍然能够继续上传,重启电脑后仍然能够上传 支持文件夹的上传,要求服务端能够保留层级结构,并且能够续传。需要支持10万个以上的文件夹上传。 支持低版本的系统和浏览器,因为这个项目的最终运行环境在政府,政府的配置都一般,职员都是办公用,内存都不大,基本上以Windows XP的系统为主。 1、介绍enctype enctype 属性规定发送到服务器之前应该如何对表单数据进行编码。 enctype作用是告知服务器请求正文的MIME类型(请求消息头content-type的作用一样) 1、1 enctype的取值有三种 值 描述 application/x-www-form-urlencoded 在发送前编码所有字符(默认) multipart/form-data 不对字符编码。每一个表单项分割为一个部件 text/plain 空格转换为 “+” 加号,但不对特殊字符编码。 1. 当enctype=’application/x-www-form-urlencoded’ 2.当enctype=’multipart/form-data’ 通过观察发现这个的请求体就发生了变化。这种请求体被称之为多部件请求体。 什么是多部件请求体:就是把每一个表单项分割为一个部件。 以请求头的content-type的boundary后面的一串随机字符串作为分割标识 普通表单项: //name的意思是文本框里面name的属性值,而admin是我们输入的文本值 Content-Disposition: form-data; name="username" admin 文件表单项 //filename的意思是:我们上传的文件名称,content-Type的意思是:MIME类型,asdasdas的意思是:文件里面的内容 Content-Disposition: form-data; name="upload"; filename="a.txt" Content-Type: text/plain asdasdas 3. 当enctype=’text/plain’ w3c称:空格会变成”+”加号,但是我这里没有发现,只有当get请求的时候,空格会变成”+”号 进入正题 完成上传需要满足3个必要的条件 提供form表单,method必须是post,因为get请求的传输数据一般为2kb,不同浏览器不一样。 form表单属性enctype的必须是multipart/form-data 提供input type=”file”类的上传输入域 大致实现原理:当enctype的值是multipart/form-data时,浏览器会把每个表单项进行分割,分割成不同的部件,以boundary的值为分割标识,这个标识的字符串是随机生成的,最后一个表单项的分割标识字符串末尾会多两个”- -“,代表结束。服务端用request.getHeader(“content-type”)获取分割字符串,然后进行解析。 代码实现 一、开发环境搭建 准备两个第三方jar包 commons-io包 commons-upload包 所有依赖包 代码实现
out.clear(); String uid = request.getHeader("uid");// String id = request.getHeader("id"); String lenSvr = request.getHeader("lenSvr"); String lenLoc = request.getHeader("lenLoc"); String blockOffset = request.getHeader("blockOffset"); String blockSize = request.getHeader("blockSize"); String blockIndex = request.getHeader("blockIndex"); String blockMd5 = request.getHeader("blockMd5"); String complete = request.getHeader("complete"); String pathSvr = ""; //参数为空 if( StringUtils.isBlank( uid ) || StringUtils.isBlank( id ) || StringUtils.isBlank( blockOffset )) { XDebug.Output("param is null");return; } boolean isMultipart = ServletFileUpload.isMultipartContent(request); FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); List files = null; try {files = upload.parseRequest(request);} catch (FileUploadException e) {out.println("read file data error:" + e.toString());return;} FileItem rangeFile = null; Iterator fileItr = files.iterator(); while (fileItr.hasNext()) { rangeFile = (FileItem) fileItr.next(); if(StringUtils.equals( rangeFile.getFieldName(),"pathSvr")) { pathSvr = rangeFile.getString(); pathSvr = PathTool.url_decode(pathSvr); } } boolean verify = false; String msg = ""; String md5Svr = ""; long blockSizeSvr = rangeFile.getSize(); if(!StringUtils.isBlank(blockMd5)){md5Svr = Md5Tool.fileToMD5(rangeFile.getInputStream());} verify = Integer.parseInt(blockSize) == blockSizeSvr; if(!verify){ msg = "block size error sizeSvr:" + blockSizeSvr + "sizeLoc:" + blockSize;} if(verify && !StringUtils.isBlank(blockMd5)) { verify = md5Svr.equals(blockMd5); if(!verify) msg = "block md5 error"; } if(verify) { FileBlockWriter res = new FileBlockWriter(); if( Integer.parseInt(blockIndex)==1) res.CreateFile(pathSvr,Long.parseLong(lenLoc)); res.write( Long.parseLong(blockOffset),pathSvr,rangeFile); up6_biz_event.file_post_block(id,Integer.parseInt(blockIndex)); JSONObject o = new JSONObject(); o.put("msg", "ok"); o.put("md5", md5Svr); o.put("offset", blockOffset); msg = o.toString(); } rangeFile.delete(); out.write(msg); %> 下载的必须条件 两个头一个流 content-type Content-Type是返回消息中非常重要的内容,表示文档内容属于什么MIME类型。 浏览器会根据Content-Type来决定如何显示返回的消息体内容。 默认值是text/html 可以使用request.getServletContext().getMimeType(“文件名”)获取MIME类型。 Content-Disposition Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。 默认值是inline,表示在浏览器窗口中打开。 服务端向客户端游览器发送文件时,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器 中显示。 如果需要提示用户保存,利用Content-Disposition进行一下处理,关键在于一定要加上attachment。 例如:Content-Disposition:attachment;filename=xxx,浏览器就会激活下载框对话框, attachment 表示附件, filname 后面跟随的是显示在下载框中的文件名称。 流 下载就是向客户端响应字节数据! 将一个文件变成字节数组, 使用 response.getOutputStream() 来响应给浏览器。 代码如下,此代码已经实现了断点续传功能,用户在下载过程可以暂停,和继续下载,对服务器造成的压力也比较小。 String fid = request.getHeader("id"); String blockIndex = request.getHeader("blockIndex");//基于1 String blockOffset = request.getHeader("blockOffset");//块偏移,相对于整个文件 String blockSize = request.getHeader("blockSize");//块大小(当前需要下载的大小) String pathSvr = request.getHeader("pathSvr");//文件在服务器的位置 pathSvr = PathTool.url_decode(pathSvr); if ( StringUtils.isBlank(fid) ||StringUtils.isBlank(blockIndex) ||StringUtils.isEmpty(blockOffset) ||StringUtils.isBlank(blockSize) ||StringUtils.isBlank(pathSvr)) { response.setStatus(500); response.setHeader("err","参数为空"); return; } File f = new File(pathSvr); //文件不存在 if(!f.exists()) { response.setStatus(500); OutputStream os = response.getOutputStream(); System.out.println(String.format("%s 文件不存在",pathSvr)); os.close(); return; } long fileLen = f.length(); response.setContentType("application/x-download"); response.setHeader("Pragma","No-cache"); response.setHeader("Cache-Control","no-cache"); response.addHeader("Content-Length",blockSize); response.setDateHeader("Expires", 0); OutputStream os = response.getOutputStream(); try { RandomAccessFile raf = new RandomAccessFile(pathSvr,"r"); int readToLen = Integer.parseInt(blockSize); int readLen = 0; raf.seek( Long.parseLong(blockOffset) );//定位索引 byte[] data = new byte[1048576]; while( readToLen > 0 ) { readLen = raf.read(data,0,Math.min(1048576,readToLen) ); readToLen -= readLen; os.write(data, 0, readLen); } os.flush(); os.close(); raf.close(); os = null; response.flushBuffer(); out.clear(); out = pageContext.pushBody(); } catch(Exception e) { response.setStatus(500); os.close(); out.close(); e.printStackTrace(); } finally { if(os != null) { os.close(); os = null; } out.clear(); out = pageContext.pushBody(); }%> 加载文件列表,在下载列表中显示出来 后端代码逻辑大部分是相同的,目前能够支持MySQL,Oracle,SQL。在使用前需要配置一下数据库,可以参考我写的这篇文章:http://blog.ncmem.com/wordpress/2019/08/12/java-http%E5%A4%A7%E6%96%87%E4%BB%B6%E6%96%AD%E7%82%B9%E7%BB%AD%E4%BC%A0%E4%B8%8A%E4%BC%A0/ 欢迎入群一起讨论“374992201” HTML5实现浏览器端大文件分片上传 标签:指示 w3c 空格 tostring false bug 搭建 parse tab 原文地址:https://www.cnblogs.com/songsu/p/12579606.html