java如何实现文件切片上传服务器+断点续传

java如何实现文件切片上传服务器+断点续传

这篇文章主要为大家展示了“java如何实现文件切片上传服务器+断点续传”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“java如何实现文件切片上传服务器+断点续传”这篇文章吧。

1.定义一个实体类

用于接收前台传过来的信息,信息应该包括总切片数,当前切片数,文件名称,文件总大小。

java如何实现文件切片上传服务器+断点续传

importcom.geely.tcm.common.annotation.Excel;importlombok.Data;importjavax.validation.constraints.Max;importjavax.validation.constraints.Min;importjavax.validation.constraints.NotNull;importjava.io.Serializable;@DatapublicclassMutilFileInfoimplementsSerializable{privatestaticfinallongserialVersionUID=1L;@NotNull(message="车型不能为空")@Excel(name="车型")privateStringmodel;@Excel(name="文件名称")privateStringname;@Excel(name="文件总大小")privateLongtotalSize;@Min(value=1,message="不能小于1")@Max(value=10000,message="不能大于10000")@Excel(name="当前分片序号")privateIntegerchunk;@Min(value=1,message="不能小于1")@Max(value=10000,message="不能大于10000")@Excel(name="分片总数")privateIntegerchunks;}

2.创建controller

用于处理前端过来的文件及信息

importcom.alibaba.fastjson.JSON;importcom.baidubce.services.bos.model.PartETag;importcom.geely.tcm.common.annotation.Log;importcom.geely.tcm.common.bean.MutilFileInfo;importcom.geely.tcm.common.bean.baidu.CustomPartETag;importcom.geely.tcm.common.bean.measure.Cmm3dModel;importcom.geely.tcm.common.configuration.RedisUtils;importcom.geely.tcm.common.enums.BusinessType;importcom.geely.tcm.common.util.StringUtils;importcom.geely.tcom.remote.api.baidu.BaiduCloudBosService;importio.swagger.annotations.Api;importio.swagger.annotations.ApiImplicitParam;importio.swagger.annotations.ApiImplicitParams;importio.swagger.annotations.ApiOperation;importlombok.extern.slf4j.Slf4j;importorg.apache.dubbo.config.annotation.DubboReference;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importorg.springframework.web.multipart.MultipartFile;importjava.util.*;importjava.util.concurrent.Semaphore;importjava.util.stream.Collectors;@Api(tags="文件上传下载服务")@RequestMapping("/threeModel")@RestController@Slf4jpublicclassModel3DController1{//DubboReference这个是远程调用,如果没有远程调用,直接Autowired就可以@DubboReference(version="${sheet.service.version}")privateBaiduCloudBosServicebaiduCloudBosService;//通过加锁保证线程安全privateSemaphorelock=newSemaphore(1);//api开头的这些是swagger用到的,如果没使用swagger可以删到PostMapping上边@ApiOperation(value="obj模型上传")@ApiImplicitParams({@ApiImplicitParam(name="totalSize",value="文件总大小",dataTypeClass=Long.class),@ApiImplicitParam(name="chunks",value="总切片数",dataTypeClass=Integer.class),@ApiImplicitParam(name="chunk",value="当前切片数",dataTypeClass=Integer.class),@ApiImplicitParam(name="model",value="车型",dataTypeClass=String.class),@ApiImplicitParam(name="file",value="切片文件",dataTypeClass=MultipartFile.class)})@Log(title="obj模型上传",businessType=BusinessType.INSERT)@PostMapping("/upload3DToBos")publicObjectupload3DToBos(MutilFileInfofileinfo,@RequestParam(required=false,value="file")MultipartFilemultipartFile){Stringmodel=fileinfo.getModel().trim();Map<String,Object>m=newHashMap();StringfileName=fileinfo.getName();if(StringUtils.isEmpty(fileName)){//判断文件名称是否为空m.put("key","error");m.put("value","文件名不能为空");returnm;}if(multipartFile==null){//判断上传过来的切片是否为空m.put("key","error");m.put("value","请上传文件");returnm;}try{StringfilePath="/test/"+model+"/"+fileName;//拼接百度云地址StringredisKey="partETags-"+model+"-"+fileName;//缓存的键Integercurrent=fileinfo.getChunk();//当前切片数Integertotal=fileinfo.getChunks();//总切片数Longsize=multipartFile.getSize();//文件大小StringuploadId=getUploadId(redisKey,filePath);//该方法获取百度云的唯一标识uploadIdbyte[]file=multipartFile.getBytes();//将切片过来的文件转成字节数组if(current==1){//这块进行断点续传,如果是第一个切片,查询一下缓存中是否有数据了,如果有返回给前台,如果没有进行上传if(getCurrentEqualsOne(redisKey).size()>0){//给前台提供是否缓存中已有数据returngetCurrentEqualsOne(redisKey);}}//切片上传百度云List<CustomPartETag>customPartETags=getPartEtags(redisKey,uploadId,filePath,file,size,current);//如果缓存中成功的切片数等于总数,那么进行合并if(customPartETags.size()==total){customPartETags=customPartETags.stream().sorted(Comparator.comparing(PartETag::getPartNumber)).collect(Collectors.toList());//进行排序baiduCloudBosService.completePart(uploadId,customPartETags,filePath);//执行合并RedisUtils.hdel(redisKey,"uploadId","customPartETags");//如果合并成功,删除缓存中的数据//入库做记录m.put("key","success");m.put("value","上传成功");returnm;}m.put("key","continue");m.put("value",current+1);returnm;}catch(Exceptione){e.printStackTrace();m.put("key","error");m.put("value","上传失败");returnm;}}/***获取part**@paramredisPartKeyredisKey*@return*/privateList<CustomPartETag>getPartEtags(StringredisPartKey,StringuploadId,StringfilePath,byte[]file,longsize,intcurrent){List<CustomPartETag>customPartETags=null;try{lock.acquire();//因为每次上传成功后都会把成功的切片存进百度云,如注解下方第四行代码,所以每次上传之前需要先取出来,将成功的切片放进去customPartETags=RedisUtils.hget(redisPartKey,"customPartETags")==null?newArrayList<>():JSON.parseArray((String)RedisUtils.hget(redisPartKey,"customPartETags"),CustomPartETag.class);//上传百度云成功之后会返回一个PartETagg对象,由于项目中存在dubbo,顾将CustomPartETag类继承PartETagg进行序列化CustomPartETagcustomPartETag=baiduCloudBosService.uploadCustomPartETag(uploadId,filePath,file,size,current);customPartETags.add(customPartETag);setPartEtagsToRedis(redisPartKey,customPartETags);}catch(InterruptedExceptione){e.printStackTrace();}finally{lock.release();}returncustomPartETags;}/***获取uploadId**@paramredisPartKeyredisKey*@paramfilePathfilePath*@return*/privateStringgetUploadId(StringredisPartKey,StringfilePath){StringuploadId=null;try{lock.acquire();//线程锁uploadId=(String)RedisUtils.hget(redisPartKey,"uploadId");//先去redis中找,是否已经有uploadIdif(StringUtils.isEmpty(uploadId)){//判断是否为空,如果为空,说明之前没上传过,为首次上传,如果已经存在,返回该uploadIduploadId=baiduCloudBosService.initMultipartUpload(filePath);//进行初始化,返回一个唯一标识uploadIdRedisUtils.hset(redisPartKey,"uploadId",uploadId,60*60);//将返回来的uploadId放到缓存中,下个切片上传直接使用这个uploadId}}catch(InterruptedExceptione){e.printStackTrace();}finally{lock.release();}returnuploadId;}/***薄墩partKey到redis**@paramredisPartKey*@paramcustomPartETags*/privatevoidsetPartEtagsToRedis(StringredisPartKey,List<CustomPartETag>customPartETags){RedisUtils.hset(redisPartKey,"customPartETags",JSON.toJSONString(customPartETags),60*60);}publicMap<String,Object>getCurrentEqualsOne(StringredisKey){Map<String,Object>m=newHashMap<>();List<CustomPartETag>customPartETags=RedisUtils.hget(redisKey,"customPartETags")==null?newArrayList<>():JSON.parseArray((String)RedisUtils.hget(redisKey,"customPartETags"),CustomPartETag.class);//给前台提供是否缓存中已有数据if(customPartETags.size()==0){returnm;}else{//缓存中有数据,返回缓存中数据intt=customPartETags.get(customPartETags.size()-1).getPartNumber();m.put("key","hasValue");m.put("value",t);returnm;}}}

3.远程的service

(如果不用远程调用,直接创建一个service就可以)

packagecom.geely.tcm.sheet.service;importcom.baidubce.services.bos.BosClient;importcom.baidubce.services.bos.model.*;importcom.geely.tcm.common.bean.baidu.BosResult;importcom.geely.tcm.common.bean.baidu.CustomPartETag;importcom.geely.tcm.common.util.StringUtils;importcom.geely.tcom.remote.api.baidu.BaiduCloudBosService;importlombok.extern.slf4j.Slf4j;importorg.apache.dubbo.config.annotation.DubboService;importorg.springframework.beans.factory.annotation.Autowired;importjava.io.*;importjava.net.URL;importjava.util.ArrayList;importjava.util.List;/***百度私有云服务操作**@author:wangbin*@date:2021/9/24*@since:1.0.0*/@Slf4j@DubboService(version="${sheet.service.version}")publicclassBaiduCloudBosServiceImpl{@AutowiredprivateBosClientbosClient;publicStringinitMultipartUpload(StringfilePath){//开始MultipartUploadInitiateMultipartUploadRequestinitiateMultipartUploadRequest=newInitiateMultipartUploadRequest(null,filePath);InitiateMultipartUploadResponseinitiateMultipartUploadResponse=bosClient.initiateMultipartUpload(initiateMultipartUploadRequest);//打印UploadId,它是区分分块上传事件的唯一标识StringuploadId=initiateMultipartUploadResponse.getUploadId();log.info("filePath={},uploadId={}",filePath,uploadId);returnuploadId;}publicCustomPartETaguploadCustomPartETag(StringuploadId,StringfilePath,byte[]file,longpartSize,intpartNumber){UploadPartRequestuploadPartRequest=newUploadPartRequest();uploadPartRequest.setBucketName(null);uploadPartRequest.setKey(filePath);uploadPartRequest.setUploadId(uploadId);InputStreaminput=newByteArrayInputStream(file);uploadPartRequest.setInputStream(input);uploadPartRequest.setPartSize(partSize);uploadPartRequest.setPartNumber(partNumber);uploadPartRequest.setMd5Digest("");UploadPartResponseuploadPartResponse=bosClient.uploadPart(uploadPartRequest);PartETagpartETag=uploadPartResponse.getPartETag();CustomPartETagcustomPartETag=newCustomPartETag();customPartETag.setETag(partETag.getETag());customPartETag.setPartNumber(partETag.getPartNumber());returncustomPartETag;}/***分块上传结束之后,当所有的数据Part验证通过后,BOS将把这些数据part组合成一个完整的Object**@paramuploadId*@paramcustomPartETags*@paramfilePath*/publicvoidcompletePart(StringuploadId,List<CustomPartETag>customPartETags,StringfilePath){//for循环转一下CustomPartETag转PartETagif(customPartETags.size()>0){List<PartETag>partETags=newArrayList<>();for(CustomPartETagc:customPartETags){PartETagpartETag=newPartETag();partETag.setETag(c.getETag());partETag.setPartNumber(c.getPartNumber());partETags.add(partETag);}CompleteMultipartUploadRequestcompleteMultipartUploadRequest=newCompleteMultipartUploadRequest(null,filePath,uploadId,partETags);CompleteMultipartUploadResponsecompleteMultipartUploadResponse=bosClient.completeMultipartUpload(completeMultipartUploadRequest);log.info("完成分片上传,filePath={},uploadId={},etag={}",filePath,uploadId,completeMultipartUploadResponse.getETag());}}}

4.定义一个实体类

继承,实现序列化,(dubbo不序列化就会传不过去)

importcom.baidubce.services.bos.model.PartETag;importjava.io.Serializable;publicclassCustomPartETagextendsPartETagimplementsSerializable{}

5.这里提供一个临时测试的前端代码

<htmllang="en"><head><metacharset="UTF-8"><title>upload</title><scriptsrc="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script></head><body><inputtype="file"name="file"id="file"><buttonid="upload"onClick="upload()">upload</button><scripttype="text/javascript">varbytesPerPiece=1024*1024;//每个文件切片大小定为1MB.vartotalPieces;functionupload(){varblob=document.getElementById("file").files[0];varstart=0;varend;varindex=1;varfilesize=blob.size;varfilename=blob.name;//计算文件切片总数totalPieces=Math.ceil(filesize/bytesPerPiece);while(start<filesize){end=start+bytesPerPiece;if(end>filesize){end=filesize;}varchunk=blob.slice(start,end);//切割文件varsliceIndex=blob.name+index;varformData=newFormData();formData.append("file",chunk,filename);formData.append("totalSize",filesize);formData.append("chunks",totalPieces);formData.append("chunk",index);formData.append("model",'kx11');$.ajax({//路径换成你的路径url:'http://127.0.0.1:6300/threeModel/upload3DToBos',type:'POST',dataType:'json',cache:false,data:formData,processData:false,contentType:false,mimeType:"multipart/form-data",success:function(result){console.log(result.value);}});start=end;index++;}}</script></body></html>

以上是“java如何实现文件切片上传服务器+断点续传”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注恰卡编程网行业资讯频道!

发布于 2021-12-22 21:56:31
收藏
分享
海报
0 条评论
59
上一篇:如何实现Android导航栏功能项的显示与屏蔽 下一篇:从log4j2到Disruptor的示例分析
目录

    0 条评论

    本站已关闭游客评论,请登录或者注册后再评论吧~

    忘记密码?

    图形验证码