上传大文件:秒级上传、断点续传、分片上传但是在文件比较大的情况下,用正常的方式上传并不是一个好的方法。毕竟,很少有人会容忍他们。一次不愉快的经历。有没有更好的上传体验?答案是肯定的,就是下面要介绍的几种上传方式。详细教程Secondpass1.什么是secondpass通俗的说就是当你上传要上传的东西时,服务器会先做MD5校验,如果服务器上有相同的东西,它会直接给你一个新的地址。其实你下载的是服务器上的同一个文件。如果想秒传,其实只要让MD5变了,就是文件本身了。修改一次(改名字不行)。比如你在一个文本文件里多加几个词,MD5就变了,秒传不出去。status,key为文件上传的md5,value为上传是否完成的flag。b.当标志为真时,上传已经完成。如果此时上传同一个文件,则进入第二次传输逻辑。如果标志位为false,表示上传还没有完成。这时候需要调用set方法保存块号文件记录的路径,其中key是上传的文件md5加上固定前缀,value是块号文件记录路径分片上传1.什么是部分上传?Part上传就是将要上传的文件按照一定的大小分成多个数据块(我们称之为Part),分别上传。上传完成后,服务器将所有上传的文件汇总整合为原始文件。二、分片上传场景1、大文件上传2、网络环境不好,存在重传风险的场景1、什么是续传?下载或上传任务(一个文件或一个压缩包)被人为地分成几个部分,每个部分使用一个线程来上传或下载。如果出现网络故障,您可以从已经上传或下载的部分继续上传或下载。下载未完成的部分,无需从头开始上传或下载。本文断点续传主要针对断点续传场景。2.应用场景断点续传可以看作是分片上传的衍生,所以断点续传可以用在任何可以使用分片上传的场景。3.实现断点续传的核心逻辑在分片上传的过程中,如果由于系统崩溃或网络中断等异常因素导致上传中断,客户端需要记录上传的进度。稍后支持再次上传时,您可以从上次上传中断的地方继续上传。为了避免客户端上传后的进度数据被删除,重新从头开始上传的问题,服务端也可以提供相应的接口,方便客户端查询上传的分片数据,让客户端知道上传的数据。分片数据,以便从下一个分片数据继续上传。4、实现方案一的流程步骤a,常规步骤将待上传文件按照一定的切分规则切分成大小相同的数据块;初始化一个分片上传任务,返回该分片上传的唯一标识;按照一定的策略(串行或并行)发送每个分片数据块;发送完成后,服务器判断上传的数据是否完整,如果完整则合成数据块得到原始文件。b.方案二、本文实现的步骤前端(client)需要按照固定大小对文件进行分片,请求后端(server)时,必须带上分片号和大小。服务端创建一个conf文件记录blocks的位置,conf文件的长度就是分片的总数,每上传一个block,就往conf文件中写入一个127,那么还没有上传的位置就是默认0,上传的是Byte。点恢复和二次传输的核心步骤)服务器根据请求数据中给出的段号和每个段的大小(段大小固定且相同)计算起始位置,写入导入文件。5.分片上传/断点上传代码的实现前端采用百度提供的webuploader插件进行分片。因为本文主要介绍服务端代码实现,webuploader如何分片,具体实现可以查看以下链接:http://fex.baidu.com/webuploader/getting-started.htmlb,后端使用了两种方法实现文件写入,一种是使用RandomAccessFile,如果对RandomAccessFile不熟悉,可以查看以下链接:https://blog.csdn.net/dimudan2015/article/details/81910690另一种是使用MappedByteBuffer,对MappedByteBuffer不熟悉的朋友,可以查看以下链接了解:https://www.jianshu.com/p/f90866dcbffc后端写操作的核心代码a,RandomAccessFile实现@UploadMode(mode=UploadModeEnum.RANDOM_ACCESS)@Slf4jpublicclassRandomAccessUploadStrategyextendsSliceUploadTemplate{@AutowiredprivateFilePathUtilfilePathUtil;@Value("${upload.chunkSize}")privatelongdefaultChunkSize;@Overridepublicbooleanupload(FileUploadRequestDTOparam){RandomAccessFileaccessTmpFile=null;try{StringuploadDirPath=filePathUtil.getPath(param);FiletmpFile=super.createTmpFile(param);accessTmpFile=newRandomFile,"rw");//这个必须和前端设置的值一致longchunkSize=Objects.isNull(param.getChunkSize())?defaultChunkSize*1024*1024:param.getChunkSize();longoffset=chunkSize*param.getChunk();//确定到该片段的偏移量accessTmpFile.seek(offset);//写入该片段数据accessTmpFile.write(param.getFile().getBytes());booleanisOk=super.checkAndSetUploadProgress(param,uploadDirPath);returnisOk;}catch(IOExceptione){log.error(e.getMessage(),e);}finally{FileUtil.close(accessTmpFile);}returnfalse;}}b、MappedByteBuffer实现方式@UploadMode(mode=UploadModeEnum.MAPPED_BYTEBUFFER)@Slf4jpublicclassMappedByteBufferUploadStrategyextendsSliceUploadTemplate{@AutowiredprivateFilePathUtilfilePathUtil;@Value("${upload.chunkSize}")privatelongdefaultChunkSize;@Overridepublicbooleanupload(FileUploadRequestDTOparam){RandomAccessFiletempRaf=null;FileChannelfileChannel=null;MappedByteBuffermappedByteBuffer=null;try{StringuploadDirPath=filePathUtil.getPath(param);FiletmpFile=super.createTmpFile(param);tempRaf=newRandomAccessFile(tmpFile,"rw");fileChannel=tempRaf.getChannel();longchunkSize=Objects.isNull(param.getChunkSize())?默认块大小*1024*1024:参数。getChunkSize();//写入该切片数据longoffset=chunkSize*param.getChunk();byte[]fileData=param.getFile().getBytes();mappedByteBuffer=fileChannel.map(FileChannel.MapMode.READ_WRITE,offset,fileData.length);mappedByteBuffer.put(fileData);booleanisOk=super.checkAndSetUploadProgress(param,uploadDirPath);returnisOk;}catch(IOExceptione){log.error(e.getMessage(),e);}最后{FileUtil.freedMappedByteBuffer(mappedByteBuffer);FileUtil.close(fileChannel);FileUtil.close(tempRaf);}returnfalse;}}c、文件操作核心模板类代码@Slf4jpublicabstractclassSliceUploadTemplateimplementsSliceUploadStrategy{publicabstractbooleanupload(FileUploadRequestDTOparam);protectedFilecreateTmpFile(FileUploadRequestDTOparam){FilePathUtilfilePathUtil=SpringContextHolder.getBean(FilePathUtil.class);param.setPath(FileUtil.withoutHeadAndTailDiagonal(param.getPath()));StringfileName=param.getFile().getOriginalFilename();StringuploadDirPath=filePathUtil.getPath(param);StringtempFileName=fileName+"_tmp";文件tmpDir=newFile(uploadDirPath);FiletmpFile=newFile(uploadDirPath,tempFileName);if(!tmpDir.exists()){tmpDir.mkdirs();}returntmpFile;}@OverridepublicFileUploadDTOsliceUpload(FileUploadRequestDTOparam){booleanisOk=this.upload(参数);if(isOk){FiletmpFile=this.createTmpFile(param);FileUploadDTOfileUploadDTO=this.saveAndFileUploadDTO(param.getFile().getOriginalFilename(),tmpFile);returnfileUploadDTO;}Stringmd5=FileMD5Util.getFileMD5(param.getFile());Map
