Java怎么将大文件分片上传(java,开发技术)

时间:2024-05-10 05:51:50 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

Java 大文件分片上传

原理:前端通过js读取文件,并将大文件按照指定大小拆分成多个分片,并且计算每个分片的MD5值。前端将每个分片分别上传到后端,后端在接收到文件之后验证当前分片的MD5值是否与上传的MD5一致,待所有分片上传完成之后后端将多个分片合并成一个大文件,并校验该文件的MD5值是否与上传时传入的MD5值一致;

首先是交互的控制器

支持文件分片上传,查询当前已经上传的分片信息,取消文件上传

packagecom.aimilin.component.system.service.modular.file.controller;importcom.aimilin.common.core.pojo.base.param.BaseParam;importcom.aimilin.common.core.pojo.response.ResponseData;importcom.aimilin.common.log.annotation.BusinessLog;importcom.aimilin.common.log.enums.LogOpTypeEnum;importcom.aimilin.common.security.annotation.Permission;importcom.aimilin.component.system.service.modular.file.param.SysPartFileParam;importcom.aimilin.component.system.service.modular.file.result.SysPartFileResult;importcom.aimilin.component.system.service.modular.file.service.SysPartFileService;importlombok.extern.slf4j.Slf4j;importorg.springframework.validation.annotation.Validated;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RestController;importjavax.annotation.Resource;/***系统大文件上传**@versionV1.0*@date2022/5/2411:22*/@Slf4j@RestControllerpublicclassSysPartFileController{@ResourceprivateSysPartFileServicesysPartFileService;/***上传大文件**/@Permission@PostMapping("/sysFileInfo/partUpload")publicResponseData<SysPartFileResult>partUpload(@Validated(BaseParam.add.class)SysPartFileParampartFile){returnResponseData.success(sysPartFileService.partUpload(partFile));}/***获取文件上传状态**/@Permission@GetMapping("/sysFileInfo/partUpload/status")publicResponseData<SysPartFileResult>getPartUploadStatus(@Validated(BaseParam.detail.class)SysPartFileParampartFile){returnResponseData.success(sysPartFileService.getPartUploadStatus(partFile));}/***获取文件上传状态**/@Permission@GetMapping("/sysFileInfo/partUpload/cancel")@BusinessLog(title="文件_上传大文件_取消",opType=LogOpTypeEnum.OTHER)publicResponseData<SysPartFileResult>cancelUpload(@Validated(BaseParam.detail.class)SysPartFileParampartFile){returnResponseData.success(sysPartFileService.cancelUpload(partFile));}}

上传文件分片参数接收

如果按照分片方式上传文件需要指定当前大文件的MD5、分片MD5、分片内容、分片大小、当前文件名称、文件总大小等信息;另外对于每个文件前端都需要生成一个唯一编码用于确定当前上传的分片属于统一文件。

packagecom.aimilin.component.system.service.modular.file.param;importjava.io.Serializable;importjava.util.Objects;importcom.baomidou.mybatisplus.core.toolkit.StringUtils;importcom.aimilin.common.core.pojo.base.param.BaseParam;importlombok.Getter;importlombok.Setter;importlombok.ToString;importorg.springframework.web.multipart.MultipartFile;importjavax.validation.constraints.NotNull;/***大文件断点续传**@versionV1.0*@date2022/5/2410:52*/@Getter@Setter@ToStringpublicclassSysPartFileParamextendsBaseParamimplementsSerializable{/***文件上传Id,前端传入的值*/@NotNull(message="uid不能为空",groups={BaseParam.detail.class,BaseParam.add.class})privateStringuid;/***上传文件名称*/privateStringfilename;/***当前文件块,从1开始*/@NotNull(message="partNumber不能为空",groups={BaseParam.add.class})privateIntegerpartNumber;/***当前分块Md5*/@NotNull(message="partMd5不能为空",groups={BaseParam.add.class})privateStringpartMd5;/***分块大小,根据totalSize和这个值你就可以计算出总共的块数。注意最后一块的大小可能会比这个要大。*/@NotNull(message="partSize不能为空",groups={BaseParam.add.class})privateLongpartSize;/***总大小*/@NotNull(message="totalSize不能为空",groups={BaseParam.add.class})privateLongtotalSize;/***文件标识,MD5指纹*/@NotNull(message="fileMd5不能为空",groups={BaseParam.add.class})privateStringfileMd5;/***二进制文件*/@NotNull(message="file不能为空",groups={BaseParam.add.class})privateMultipartFilefile;/***总块数,(int)totalSize/partSize最后一个模块要大一点;**@return结果*/publicIntegergetTotalParts(){if(Objects.isNull(totalSize)||Objects.isNull(partSize)){return0;}returnnewDouble(Math.ceil(totalSize*1.0/partSize)).intValue();}publicStringgetFilename(){if(StringUtils.isBlank(this.filename)&&Objects.isNull(this.file)){returnnull;}returnStringUtils.isBlank(this.filename)?this.file.getOriginalFilename():this.filename;}}

至于代码中的 BaseParam 类,只是定义了一些验证的分组,类似以下代码:

/***参数校验分组:分页*/public@interfacepage{}/***参数校验分组:列表*/public@interfacelist{}/***参数校验分组:下拉*/public@interfacedropDown{}/***参数校验分组:增加*/public@interfaceadd{}

大文件分片上传服务类实现

也是定义了三个接口,分片上传、查询当前已上传的分片、取消文件上传

packagecom.aimilin.component.system.service.modular.file.service;importcom.aimilin.component.system.service.modular.file.param.SysPartFileParam;importcom.aimilin.component.system.service.modular.file.result.SysPartFileResult;/***块文件上传**@versionV1.0*@date2022/5/2410:59*/publicinterfaceSysPartFileService{/***文件块上传公共前缀*/publicstaticfinalStringPART_FILE_KEY="PART_FILE";/***文件块上传*1.将上传文件按照partSize拆分成多个文件块*2.判断当前文件块是否已经上传*3.未上传,则上传当前文本块*4.已上传则不处理*5.统计当前文本块上传进度信息*6.判断所有文本块是否已经上传完成,如果上传完成则触发文件合并*/publicSysPartFileResultpartUpload(SysPartFileParampartFile);/***获取文件上传状态**@parampartFile上传文件信息*@return文件上传状态结果*/publicSysPartFileResultgetPartUploadStatus(SysPartFileParampartFile);/***取消文件上传**@parampartFile上传文件信息*@return文件上传状态结果*/publicSysPartFileResultcancelUpload(SysPartFileParampartFile);}

服务实现类:

packagecom.aimilin.component.system.service.modular.file.service.impl;importcn.hutool.core.io.FileUtil;importcom.baomidou.mybatisplus.core.toolkit.IdWorker;importcom.aimilin.common.base.file.FilePartOperator;importcom.aimilin.common.base.file.param.AbortMultipartUploadResult;importcom.aimilin.common.base.file.param.CompleteFileUploadPart;importcom.aimilin.common.base.file.param.FileUploadPart;importcom.aimilin.common.base.file.param.FileUploadPartResult;importcom.aimilin.common.cache.RedisService;importcom.aimilin.common.core.consts.CommonConstant;importcom.aimilin.common.core.context.login.LoginContextHolder;importcom.aimilin.common.core.exception.ServiceException;importcom.aimilin.component.system.service.modular.file.convert.SysPartFileConvert;importcom.aimilin.component.system.service.modular.file.entity.SysFileInfo;importcom.aimilin.component.system.service.modular.file.enums.SysFileInfoExceptionEnum;importcom.aimilin.component.system.service.modular.file.enums.SysPartFileEnum;importcom.aimilin.component.system.service.modular.file.param.SysPartFileParam;importcom.aimilin.component.system.service.modular.file.result.SysPartFileCache;importcom.aimilin.component.system.service.modular.file.result.SysPartFileCache.FileInfo;importcom.aimilin.component.system.service.modular.file.result.SysPartFileCache.SysFilePart;importcom.aimilin.component.system.service.modular.file.result.SysPartFileResult;importcom.aimilin.component.system.service.modular.file.service.SysFileInfoService;importcom.aimilin.component.system.service.modular.file.service.SysPartFileService;importlombok.extern.slf4j.Slf4j;importorg.apache.commons.io.FilenameUtils;importorg.redisson.api.RLock;importorg.redisson.api.RedissonClient;importorg.springframework.stereotype.Service;importorg.springframework.web.multipart.MultipartFile;importjavax.annotation.Resource;importjava.io.IOException;importjava.util.*;importjava.util.concurrent.TimeUnit;importstaticcom.aimilin.component.system.service.config.FileConfig.DEFAULT_BUCKET;/***大文件上传功能服务实现**@versionV1.0*@date2022/5/2411:53*/@Slf4j@ServicepublicclassSysPartFileServiceImplimplementsSysPartFileService{@ResourceprivateFilePartOperatorfileOperator;@ResourceprivateRedisServiceredisService;@ResourceprivateSysFileInfoServicesysFileInfoService;@ResourceprivateRedissonClientredisson;/***文件块上传*1.将上传文件按照partSize拆分成多个文件块*2.判断当前文件块是否已经上传*3.未上传,则上传当前文本块*4.已上传则不处理*5.统计当前文本块上传进度信息*6.判断所有文本块是否已经上传完成,如果上传完成则触发文件合并**@parampartFile上传文件*@returnSysPartFileResult文件上传结果*/@OverridepublicSysPartFileResultpartUpload(SysPartFileParampartFile){MultipartFilefile=partFile.getFile();log.info("分块上传文件:{},partNumber:{}/{},partSize:{}/{}",partFile.getFilename(),partFile.getPartNumber(),partFile.getTotalParts(),file.getSize(),partFile.getPartSize());SysPartFileResultpartUploadStatus=this.getPartUploadStatus(partFile);//已经上传该部分则直接返回当前文件状态if(SysPartFileEnum.SUCCESS.getCode().equals(partUploadStatus.getPartState())){returnpartUploadStatus;}//上传分片文件FileUploadPartfileUploadPart=this.getFileUploadPart(partFile);try{FileUploadPartResultuploadPartResult=fileOperator.uploadPart(fileUploadPart);this.setPartUploadStatus(partFile,uploadPartResult);}catch(Exceptione){log.error("文件分片上传失败,请求:{}:{}",partFile,e.getMessage(),e);thrownewServiceException(SysFileInfoExceptionEnum.FILE_OSS_ERROR);}returnthis.getPartUploadStatus(partFile);}/***获取文件上传状态**@parampartFile上传文件信息*@return文件上传状态结果*/@OverridepublicSysPartFileResultgetPartUploadStatus(SysPartFileParampartFile){SysPartFileCachefileCache=redisService.getCacheObject(getPartFileKey(partFile.getUid()));SysPartFileResultresult;//如果没有上传过则返回默认值if(Objects.isNull(fileCache)){result=SysPartFileConvert.INSTANCE.toSysPartFileResult(partFile);result.setFileState(SysPartFileEnum.NOT_EXISTS.getCode());result.setPartState(SysPartFileEnum.NOT_EXISTS.getCode());}else{result=SysPartFileConvert.INSTANCE.toSysPartFileResult(fileCache,fileCache.getFilePart(partFile.getPartNumber()));}returnresult;}/***取消文件上传**@parampartFile上传文件信息*@return文件上传状态结果*/@OverridepublicSysPartFileResultcancelUpload(SysPartFileParampartFile){StringcacheKey=getPartFileKey(partFile.getUid());SysPartFileCachefileCache=redisService.getCacheObject(cacheKey);if(Objects.isNull(fileCache)){thrownewServiceException(SysFileInfoExceptionEnum.NOT_EXISTED_FILE);}SysPartFileCache.FileInfofileInfo=fileCache.getFileInfo();fileOperator.abortMultipartUpload(fileInfo.getBucketName(),fileInfo.getObjectName(),fileInfo.getUploadId());log.info("取消文件上传:{}",partFile.getUid());SysPartFileResultsysPartFileResult=SysPartFileConvert.INSTANCE.toSysPartFileResult(partFile);sysPartFileResult.setFileState(SysPartFileEnum.CANCELED.getCode());redisService.deleteObject(cacheKey);returnsysPartFileResult;}/***文件分片上传,设置文件分片信息**@parampartFile分片文件参数*@paramuploadPartResult文件上传结果信息*/privatevoidsetPartUploadStatus(SysPartFileParampartFile,FileUploadPartResultuploadPartResult){StringredisKey=getPartFileKey(partFile.getUid());if(!redisService.hasKey(redisKey)){thrownewServiceException(SysFileInfoExceptionEnum.FILE_CACHE_ERROR);}RLocklock=redisson.getLock(CommonConstant.getLockKey(redisKey));try{lock.lock();SysPartFileCachefileCache=redisService.getCacheObject(redisKey);Set<SysFilePart>filePartList=fileCache.getFilePartList();if(Objects.isNull(filePartList)){filePartList=newHashSet<>();fileCache.setFilePartList(filePartList);}SysFilePartsysFilePart=newSysFilePart();sysFilePart.setPartNumber(partFile.getPartNumber());sysFilePart.setPartState(SysPartFileEnum.SUCCESS.getCode());sysFilePart.setPartMd5(partFile.getPartMd5());sysFilePart.setPartSize(partFile.getFile().getSize());sysFilePart.setFileUploadPartResult(uploadPartResult);filePartList.add(sysFilePart);fileCache.setFileState(SysPartFileEnum.UPLOADING.getCode());//所有文本块都已经上传完成if(newHashSet<>(fileCache.getUploadedParts()).size()==fileCache.getTotalParts()){CompleteFileUploadPartcompleteFileUploadPart=SysPartFileConvert.INSTANCE.toCompleteFileUploadPart(fileCache);fileOperator.completeMultipartUpload(completeFileUploadPart);log.info("文件合并完成:{},part:{}/{}",partFile.getFilename(),partFile.getPartNumber(),partFile.getTotalParts());this.saveFileInfo(partFile,fileCache);fileCache.setFileState(SysPartFileEnum.SUCCESS.getCode());redisService.setCacheObject(redisKey,fileCache,1L,TimeUnit.DAYS);}else{redisService.setCacheObject(redisKey,fileCache);}}catch(Exceptione){log.error("设置文件分片上传状态异常,{},上传结果:{}",partFile,uploadPartResult,e);thrownewServiceException(SysFileInfoExceptionEnum.PART_FILE_SET_STATE_ERROR);}finally{lock.unlock();}}/***保存文件信息到数据库**@parampartFile分片文件*@paramfileCache文件缓存对象*/privatevoidsaveFileInfo(SysPartFileParampartFile,SysPartFileCachefileCache){SysFileInfosysFileInfo=newSysFileInfo();sysFileInfo.setId(Objects.isNull(fileCache.getFileId())?IdWorker.getId():fileCache.getFileId());sysFileInfo.setFileLocation(fileOperator.getFileLocation().getCode());sysFileInfo.setFileBucket(fileCache.getFileInfo().getBucketName());sysFileInfo.setFileOriginName(fileCache.getFilename());sysFileInfo.setFileSuffix(FilenameUtils.getExtension(fileCache.getFileInfo().getObjectName()));sysFileInfo.setFileSizeKb(SysFileUtils.getFileSizeKb(fileCache.getTotalSize()));sysFileInfo.setFileSizeInfo(FileUtil.readableFileSize(fileCache.getTotalSize()));sysFileInfo.setFileObjectName(fileCache.getFileInfo().getObjectName());booleansave=sysFileInfoService.save(sysFileInfo);log.info("保存文件信息完成:{},结果:{}",partFile.getFilename(),save);}/***获取文件上传分片信息**@parampartFile分片文件参数*@return需要上传的分片文件信息*/privateFileUploadPartgetFileUploadPart(SysPartFileParampartFile){try{SysPartFileCachefileCache=redisService.getCacheObject(getPartFileKey(partFile.getUid()));if(Objects.isNull(fileCache)){fileCache=this.initSysPartFileCache(partFile);}returnSysPartFileConvert.INSTANCE.toFileUploadPart(fileCache.getFileInfo(),partFile);}catch(IOExceptione){log.error("获取文件分片对象异常:{}",e.getMessage(),e);thrownewServiceException(SysFileInfoExceptionEnum.FILE_STREAM_ERROR);}}/***初始化文件缓存对象,进入该方法说明缓存为空*@parampartFile分片文件*/privateSysPartFileCacheinitSysPartFileCache(SysPartFileParampartFile){Stringkey=getPartFileKey(partFile.getUid());RLocklock=redisson.getLock(CommonConstant.getLockKey(key));try{lock.lock();SysPartFileCachefileCache=redisService.getCacheObject(key);if(Objects.isNull(fileCache)){LongfileId=IdWorker.getId();StringobjectName=SysFileUtils.getFileObjectName(partFile.getFilename(),fileId);StringuploadId=fileOperator.initiateMultipartUpload(DEFAULT_BUCKET,objectName);fileCache=SysPartFileConvert.INSTANCE.toSysPartFileCache(partFile);fileCache.setFileState(SysPartFileEnum.UPLOADING.getCode());fileCache.setFileInfo(newFileInfo(DEFAULT_BUCKET,objectName,uploadId));fileCache.setFileId(fileId);redisService.setCacheObject(getPartFileKey(partFile.getUid()),fileCache);}returnfileCache;}catch(Exceptione){log.error("文件缓存初始化异常:{}",partFile,e);thrownewServiceException(SysFileInfoExceptionEnum.PART_FILE_INIT_CACHE_ERROR);}finally{lock.unlock();}}/***获取文件缓存key**@paramfileId文件Id*@return%s:%s:%s*/privateStringgetPartFileKey(StringfileId){returnString.format("%s:%s:%s",PART_FILE_KEY,LoginContextHolder.me().getSysLoginUserId(),fileId);}}

文件分片上传定义公共服务类接口

packagecom.aimilin.common.base.file;importcom.aimilin.common.base.file.param.*;/***大文件分片操作服务类**@versionV1.0*@date2022/5/2416:56*/publicinterfaceFilePartOperatorextendsFileOperator{/***初始化分片文件上传**@parambucketName文件桶*@paramkey文件key*@return本次文件上传唯一标识*/StringinitiateMultipartUpload(StringbucketName,Stringkey);/***上传分片文件**@paramfileUploadPart分片文件参数*@return上传结果*/FileUploadPartResultuploadPart(FileUploadPartfileUploadPart);/***完成分片上传**@paramcompleteFileUploadPart请求对象*@return结果信息*/CompleteFileUploadPartResultcompleteMultipartUpload(CompleteFileUploadPartcompleteFileUploadPart);/***取消文件分片上传**@parambucketName文件桶*@paramobjectName对象key*@paramuploadId上传ID*@return*/voidabortMultipartUpload(StringbucketName,StringobjectName,StringuploadId);}/***文件分片上传取消**@versionV1.0*@date2022/5/2420:32*/publicclassAbortMultipartUploadResult{}/***完成分片上传**@versionV1.0*@date2022/5/2420:07*/@Getter@Setter@ToStringpublicclassCompleteFileUploadPartimplementsSerializable{privateStringbucketName;privateStringobjectName;privateStringuploadId;privateList<FileUploadPartResult>partETags;}/***分片上传结果**@versionV1.0*@date2022/5/2420:08*/@Getter@Setter@ToStringpublicclassCompleteFileUploadPartResultimplementsSerializable{privateStringbucketName;privateStringobjectName;privateStringlocation;privateStringeTag;}/***文件分片上传请求参数**@versionV1.0*@date2022/5/2417:00*/@Getter@Setter@ToStringpublicclassFileUploadPartimplementsSerializable{/***文件桶*/privateStringbucketName;/***文件key*/privateStringobjectName;/***文件上传ID*/privateStringuploadId;/***分片大小,设置分片大小。除了最后一个分片没有大小限制,其他的分片最小为100KB*/privateLongpartSize;/***设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出此范围,OSS将返回InvalidArgument错误码。*/privateIntegerpartNumber;/***分片Md5签名*/privateStringpartMd5;/***分片文件内容*/@JsonIgnore@JSONField(deserialize=false,serialize=false)privateInputStreampartContent;}/***文件分片上传结果**@versionV1.0*@date2022/5/2417:01*/@Getter@Setter@ToStringpublicclassFileUploadPartResultimplementsSerializable{/***分块编号*/privateIntegerpartNumber;/***当前分片大小*/privateLongpartSize;/***上传结果tag*/privateStringpartETag;}

文件分片上传文件操作接口实现类

这里风两种实现,1:本地文件上传,2:oss对象存储方式分片上传

/***本地文件上传操作**/@Slf4jpublicclassLocalFileOperatorimplementsFilePartOperator{@OverridepublicFileLocationEnumgetFileLocation(){returnFileLocationEnum.LOCAL;}privatefinalLocalFilePropertieslocalFileProperties;privateStringcurrentSavePath="";privateDictlocalClient;publicLocalFileOperator(LocalFilePropertieslocalFileProperties){this.localFileProperties=localFileProperties;initClient();}@OverridepublicvoidinitClient(){if(SystemUtil.getOsInfo().isWindows()){StringsavePathWindows=localFileProperties.getLocalFileSavePathWin();if(!FileUtil.exist(savePathWindows)){FileUtil.mkdir(savePathWindows);}currentSavePath=savePathWindows;}else{StringsavePathLinux=localFileProperties.getLocalFileSavePathLinux();if(!FileUtil.exist(savePathLinux)){FileUtil.mkdir(savePathLinux);}currentSavePath=savePathLinux;}localClient=Dict.create();localClient.put("currentSavePath",currentSavePath);localClient.put("localFileProperties",localFileProperties);}@OverridepublicvoiddestroyClient(){//empty}@OverridepublicObjectgetClient(){//emptyreturnlocalClient;}@OverridepublicbooleandoesBucketExist(StringbucketName){StringabsolutePath=currentSavePath+File.separator+bucketName;returnFileUtil.exist(absolutePath);}@OverridepublicvoidsetBucketAcl(StringbucketName,BucketAuthEnumbucketAuthEnum){//empty}@OverridepublicbooleanisExistingFile(StringbucketName,Stringkey){returnFileUtil.exist(this.getAbsolutePath(bucketName,key));}@OverridepublicvoidstorageFile(StringbucketName,Stringkey,byte[]bytes){//判断bucket存在不存在StringbucketPath=currentSavePath+File.separator+bucketName;if(!FileUtil.exist(bucketPath)){FileUtil.mkdir(bucketPath);}//存储文件FileUtil.writeBytes(bytes,this.getAbsolutePath(bucketName,key));}@OverridepublicvoidstorageFile(StringbucketName,Stringkey,InputStreaminputStream,longfileSize){//判断bucket存在不存在StringbucketPath=currentSavePath+File.separator+bucketName;if(!FileUtil.exist(bucketPath)){FileUtil.mkdir(bucketPath);}//存储文件FileUtil.writeFromStream(inputStream,this.getAbsolutePath(bucketName,key));}@Overridepublicbyte[]getFileBytes(StringbucketName,Stringkey){//判断文件存在不存在StringabsoluteFile=this.getAbsolutePath(bucketName,key);if(!FileUtil.exist(absoluteFile)){Stringmessage=StrUtil.format("文件不存在,bucket={},key={}",bucketName,key);thrownewFileServiceException(message);}else{returnFileUtil.readBytes(absoluteFile);}}@OverridepublicvoidsetFileAcl(StringbucketName,Stringkey,BucketAuthEnumbucketAuthEnum){//empty}@OverridepublicvoidcopyFile(StringoriginBucketName,StringoriginFileKey,StringnewBucketName,StringnewFileKey){//判断文件存在不存在StringoriginFile=this.getAbsolutePath(originBucketName,originFileKey);if(!FileUtil.exist(originFile)){Stringmessage=StrUtil.format("源文件不存在,bucket={},key={}",originBucketName,originFileKey);thrownewFileServiceException(message);}else{//拷贝文件StringdestFile=this.getAbsolutePath(newBucketName,newFileKey);FileUtil.copy(originFile,destFile,true);}}@OverridepublicStringgetFileAuthUrl(StringbucketName,Stringkey,LongtimeoutMillis){//emptyreturnnull;}@OverridepublicvoiddeleteFile(StringbucketName,Stringkey){//判断文件存在不存在Stringfile=this.getAbsolutePath(bucketName,key);if(!FileUtil.exist(file)){return;}//删除文件FileUtil.del(file);}/***初始化分片文件上传**@parambucketName文件桶*@paramkey文件key*@return本次文件上传唯一标识*/@OverridepublicStringinitiateMultipartUpload(StringbucketName,Stringkey){returnFileNameUtil.getName(key);}/***上传分片文件**@paramfileUploadPart分片文件参数*@return上传结果*/@OverridepublicFileUploadPartResultuploadPart(FileUploadPartfileUploadPart){StringpartName=fileUploadPart.getObjectName()+"."+fileUploadPart.getPartNumber();this.storageFile(fileUploadPart.getBucketName(),partName,fileUploadPart.getPartContent(),fileUploadPart.getPartSize());FileUploadPartResultresult=newFileUploadPartResult();result.setPartNumber(fileUploadPart.getPartNumber());result.setPartSize(fileUploadPart.getPartSize());result.setPartETag(partName);//TODO正常文件上传完成之后需要验证文件的分片的MD5值是否与前端传入的值一样returnresult;}/***完成分片上传**@paramcompleteFileUploadPart请求对象*@return结果信息*/@OverridepublicCompleteFileUploadPartResultcompleteMultipartUpload(CompleteFileUploadPartcompleteFileUploadPart){try{List<FileUploadPartResult>partETags=completeFileUploadPart.getPartETags();Stringpath=this.getAbsolutePath(completeFileUploadPart.getBucketName(),completeFileUploadPart.getObjectName());partETags.sort((o1,o2)->{Stringp1=FileNameUtil.extName(o1.getPartETag());Stringp2=FileNameUtil.extName(o2.getPartETag());returnInteger.valueOf(p1).compareTo(Integer.valueOf(p2));});Files.createFile(Paths.get(path));partETags.forEach(c->{try{PathpartPath=Paths.get(this.getAbsolutePath(completeFileUploadPart.getBucketName(),c.getPartETag()));Files.write(Paths.get(path),Files.readAllBytes(partPath),StandardOpenOption.APPEND);Files.delete(partPath);}catch(IOExceptione){log.error("合并文件失败:{}",e.getMessage(),e);thrownewFileServiceException(e.getMessage());}});//文件合并完成之后需要校验文件的MD5值是否与前端传入的一致returnnewCompleteFileUploadPartResult();}catch(IOExceptione){log.error("合并文件失败:{}",e.getMessage(),e);thrownewFileServiceException(e.getMessage());}}/***取消文件分片上传**@parambucketName文件桶*@paramobjectName对象key*@paramuploadId上传ID*@return*/@OverridepublicvoidabortMultipartUpload(StringbucketName,StringobjectName,StringuploadId){try{Pathfolder=Paths.get(this.getAbsolutePath(bucketName,objectName)).getParent();StringpartName=objectName+".";Files.list(folder).filter(path->StrUtil.contains(path.toString(),partName)).forEach(path->{try{Files.delete(path);}catch(IOExceptione){log.warn("删除分片文件失败:{}",path);}});}catch(IOExceptione){log.error("取消文件分片上传异常:{}",e.getMessage(),e);thrownewFileServiceException(e.getMessage());}}/***获取文件绝对路径**@parambucketName文件桶*@paramkey对象key*@return*/privateStringgetAbsolutePath(StringbucketName,Stringkey){returncurrentSavePath+File.separator+bucketName+File.separator+key;}}

OSS阿里云对象存储分片上传实现

/***阿里云文件操作**/@Slf4jpublicclassAliyunFileOperatorimplementsFilePartOperator{@OverridepublicFileLocationEnumgetFileLocation(){returnFileLocationEnum.ALIYUN;}/***阿里云文件操作客户端*/privateOSSossClient;/***阿里云oss的配置*/privatefinalAliyunOssPropertiesaliyunOssProperties;publicAliyunFileOperator(AliyunOssPropertiesaliyunOssProperties){this.aliyunOssProperties=aliyunOssProperties;this.initClient();}@OverridepublicvoidinitClient(){Stringendpoint=aliyunOssProperties.getEndPoint();StringaccessKeyId=aliyunOssProperties.getAccessKeyId();StringaccessKeySecret=aliyunOssProperties.getAccessKeySecret();//创建OSSClient实例。ossClient=newOSSClientBuilder().build(endpoint,accessKeyId,accessKeySecret);}@OverridepublicvoiddestroyClient(){ossClient.shutdown();}@OverridepublicObjectgetClient(){returnossClient;}@OverridepublicbooleandoesBucketExist(StringbucketName){try{returnossClient.doesBucketExist(bucketName);}catch(OSSExceptione){thrownewAliyunFileServiceException(e);}catch(ClientExceptione){thrownewAliyunFileServiceException(e);}}@OverridepublicvoidsetBucketAcl(StringbucketName,BucketAuthEnumbucketAuthEnum){try{if(bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)){ossClient.setBucketAcl(bucketName,CannedAccessControlList.Private);}elseif(bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)){ossClient.setBucketAcl(bucketName,CannedAccessControlList.PublicRead);}elseif(bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)){ossClient.setBucketAcl(bucketName,CannedAccessControlList.PublicReadWrite);}}catch(OSSExceptione){thrownewAliyunFileServiceException(e);}catch(ClientExceptione){thrownewAliyunFileServiceException(e);}}@OverridepublicbooleanisExistingFile(StringbucketName,Stringkey){try{returnossClient.doesObjectExist(bucketName,key);}catch(OSSExceptione){thrownewAliyunFileServiceException(e);}catch(ClientExceptione){thrownewAliyunFileServiceException(e);}}@OverridepublicvoidstorageFile(StringbucketName,Stringkey,byte[]bytes){try{ossClient.putObject(bucketName,key,newByteArrayInputStream(bytes));}catch(OSSExceptione){thrownewAliyunFileServiceException(e);}catch(ClientExceptione){thrownewAliyunFileServiceException(e);}}@OverridepublicvoidstorageFile(StringbucketName,Stringkey,InputStreaminputStream,longfileSize){try{StringcontentType="application/octet-stream";if(key.contains(".")){contentType=MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(key);}ObjectMetadatametadata=newObjectMetadata();metadata.setContentType(contentType);metadata.setContentLength(fileSize);PutObjectRequestputObjectRequest=newPutObjectRequest(bucketName,key,inputStream,metadata);ossClient.putObject(putObjectRequest);}catch(OSSExceptione){thrownewAliyunFileServiceException(e);}catch(ClientExceptione){thrownewAliyunFileServiceException(e);}}@Overridepublicbyte[]getFileBytes(StringbucketName,Stringkey){InputStreamobjectContent=null;try{OSSObjectossObject=ossClient.getObject(bucketName,key);objectContent=ossObject.getObjectContent();returnIoUtil.readBytes(objectContent);}catch(OSSExceptione){thrownewAliyunFileServiceException(e);}catch(ClientExceptione){thrownewAliyunFileServiceException(e);}finally{IoUtil.close(objectContent);}}@OverridepublicvoidsetFileAcl(StringbucketName,Stringkey,BucketAuthEnumbucketAuthEnum){try{if(bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)){ossClient.setObjectAcl(bucketName,key,CannedAccessControlList.Private);}elseif(bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)){ossClient.setObjectAcl(bucketName,key,CannedAccessControlList.PublicRead);}elseif(bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)){ossClient.setObjectAcl(bucketName,key,CannedAccessControlList.PublicReadWrite);}}catch(OSSExceptione){thrownewAliyunFileServiceException(e);}catch(ClientExceptione){thrownewAliyunFileServiceException(e);}}@OverridepublicvoidcopyFile(StringoriginBucketName,StringoriginFileKey,StringnewBucketName,StringnewFileKey){try{ossClient.copyObject(originBucketName,originFileKey,newBucketName,newFileKey);}catch(OSSExceptione){thrownewAliyunFileServiceException(e);}catch(ClientExceptione){thrownewAliyunFileServiceException(e);}}@OverridepublicStringgetFileAuthUrl(StringbucketName,Stringkey,LongtimeoutMillis){try{Dateexpiration=newDate(newDate().getTime()+timeoutMillis);URLurl=ossClient.generatePresignedUrl(bucketName,key,expiration);returnurl.toString();}catch(OSSExceptione){thrownewAliyunFileServiceException(e);}catch(ClientExceptione){thrownewAliyunFileServiceException(e);}}@OverridepublicvoiddeleteFile(StringbucketName,Stringkey){ossClient.deleteObject(bucketName,key);}/***初始化分片文件上传**@parambucketName文件桶*@paramkey文件key*@return本次文件上传唯一标识*/@OverridepublicStringinitiateMultipartUpload(StringbucketName,Stringkey){InitiateMultipartUploadRequestrequest=newInitiateMultipartUploadRequest(bucketName,key);InitiateMultipartUploadResultresult=ossClient.initiateMultipartUpload(request);log.info("阿里云初始化分片文件上传:{}",key);returnresult.getUploadId();}/***上传分片文件**@paramfileUploadPart分片文件参数*@return上传结果*/@OverridepublicFileUploadPartResultuploadPart(FileUploadPartfileUploadPart){UploadPartRequestrequest=AliyunConvert.INSTANCE.convert(fileUploadPart);UploadPartResultresult=ossClient.uploadPart(request);FileUploadPartResultconvert=AliyunConvert.INSTANCE.convert(result);convert.setPartSize(fileUploadPart.getPartSize());log.info("阿里云分片文件上传:{},结果:{}",fileUploadPart,request);returnconvert;}/***完成分片上传**@paramcompleteFileUploadPart请求对象*@return结果信息*/@OverridepublicCompleteFileUploadPartResultcompleteMultipartUpload(CompleteFileUploadPartcompleteFileUploadPart){List<PartETag>tags=newArrayList<>();for(FileUploadPartResultpartETag:completeFileUploadPart.getPartETags()){tags.add(newPartETag(partETag.getPartNumber(),partETag.getPartETag()));}CompleteMultipartUploadRequestrequest=newCompleteMultipartUploadRequest(completeFileUploadPart.getBucketName(),completeFileUploadPart.getObjectName(),completeFileUploadPart.getUploadId(),tags);CompleteMultipartUploadResultresult=ossClient.completeMultipartUpload(request);log.info("京东云合并文件:{},结果:{}",completeFileUploadPart,result);returnAliyunConvert.INSTANCE.convert(result);}/***取消文件分片上传**@parambucketName文件桶*@paramobjectName对象key*@paramuploadId上传ID*@return*/@OverridepublicvoidabortMultipartUpload(StringbucketName,StringobjectName,StringuploadId){AbortMultipartUploadRequestrequest=newAbortMultipartUploadRequest(bucketName,objectName,uploadId);ossClient.abortMultipartUpload(request);}}

京东云对象存储实现

packagecom.aimilin.common.base.file.modular.jdcloud;importcn.hutool.core.io.IoUtil;importcom.amazonaws.SdkClientException;importcom.amazonaws.services.s3.model.*;importcom.aimilin.common.base.file.FileOperator;importcom.aimilin.common.base.file.FilePartOperator;importcom.aimilin.common.base.file.common.enums.BucketAuthEnum;importcom.aimilin.common.base.file.common.enums.FileLocationEnum;importcom.aimilin.common.base.file.modular.jdcloud.exp.JdCloudFileServiceException;importcom.aimilin.common.base.file.modular.jdcloud.prop.JdCloudConvert;importcom.aimilin.common.base.file.modular.jdcloud.prop.JdCloudOssProperties;importcom.amazonaws.auth.AWSCredentialsProvider;importcom.amazonaws.auth.AWSStaticCredentialsProvider;importcom.amazonaws.client.builder.AwsClientBuilder;importcom.amazonaws.services.s3.AmazonS3;importcom.amazonaws.services.s3.AmazonS3Client;importcom.amazonaws.auth.AWSCredentials;importcom.amazonaws.auth.BasicAWSCredentials;importcom.amazonaws.ClientConfiguration;importcom.aimilin.common.base.file.param.*;importlombok.extern.slf4j.Slf4j;importjavax.activation.MimetypesFileTypeMap;importjava.io.ByteArrayInputStream;importjava.io.InputStream;importjava.net.URL;importjava.util.ArrayList;importjava.util.Date;importjava.util.List;/***京东云对象存储**@versionV1.0*/@Slf4jpublicclassJdCloudFileOperatorimplementsFilePartOperator{@OverridepublicFileLocationEnumgetFileLocation(){returnFileLocationEnum.JDCLOUD;}/***京东云客户端*/privateAmazonS3ossClient;/***京东云oss的配置*/privatefinalJdCloudOssPropertiesjdCloudOssProperties;/****@paramjdCloudOssProperties*/publicJdCloudFileOperator(JdCloudOssPropertiesjdCloudOssProperties){this.jdCloudOssProperties=jdCloudOssProperties;this.initClient();}@OverridepublicvoidinitClient(){ClientConfigurationconfig=newClientConfiguration();AwsClientBuilder.EndpointConfigurationendpointConfig=newAwsClientBuilder.EndpointConfiguration(jdCloudOssProperties.getEndPoint(),jdCloudOssProperties.getSigningRegion());AWSCredentialsawsCredentials=newBasicAWSCredentials(jdCloudOssProperties.getAccessKeyID(),jdCloudOssProperties.getAccessKeySecret());AWSCredentialsProviderawsCredentialsProvider=newAWSStaticCredentialsProvider(awsCredentials);ossClient=AmazonS3Client.builder().withEndpointConfiguration(endpointConfig).withClientConfiguration(config).withCredentials(awsCredentialsProvider).disableChunkedEncoding().build();}@OverridepublicvoiddestroyClient(){ossClient.shutdown();}@OverridepublicObjectgetClient(){returnossClient;}@OverridepublicbooleandoesBucketExist(StringbucketName){returnossClient.doesBucketExistV2(bucketName);}@OverridepublicvoidsetBucketAcl(StringbucketName,BucketAuthEnumbucketAuthEnum){try{if(bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)){ossClient.setBucketAcl(bucketName,CannedAccessControlList.Private);}elseif(bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)){ossClient.setBucketAcl(bucketName,CannedAccessControlList.PublicRead);}elseif(bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)){ossClient.setBucketAcl(bucketName,CannedAccessControlList.PublicReadWrite);}}catch(Exceptione){log.error("JdCloud-oss-设置预定义策略异常",e);thrownewJdCloudFileServiceException(e);}}@OverridepublicbooleanisExistingFile(StringbucketName,Stringkey){try{returnossClient.doesObjectExist(bucketName,key);}catch(Exceptione){log.error("JdCloud-oss-判断是否存在文件异常",e);thrownewJdCloudFileServiceException(e);}}@OverridepublicvoidstorageFile(StringbucketName,Stringkey,byte[]bytes){try{InputStreamis=newByteArrayInputStream(bytes);ObjectMetadatametadata=newObjectMetadata();metadata.setContentType("text/plain");metadata.setContentLength((long)bytes.length);ossClient.putObject(bucketName,key,is,metadata);}catch(Exceptione){log.error("JdCloud-oss-存储文件异常",e);thrownewJdCloudFileServiceException(e);}}@OverridepublicvoidstorageFile(StringbucketName,Stringkey,InputStreaminputStream,longfileSize){try{StringcontentType="application/octet-stream";if(key.contains(".")){contentType=MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(key);}ObjectMetadatametadata=newObjectMetadata();metadata.setContentType(contentType);metadata.setContentLength(fileSize);ossClient.putObject(bucketName,key,inputStream,metadata);}catch(Exceptione){log.error("JdCloud-oss-存储文件异常",e);thrownewJdCloudFileServiceException(e);}}@Overridepublicbyte[]getFileBytes(StringbucketName,Stringkey){InputStreamobjectContent=null;try{S3Objects3Object=ossClient.getObject(bucketName,key);objectContent=s3Object.getObjectContent();returnIoUtil.readBytes(objectContent);}catch(Exceptione){log.error("JdCloud-oss-获取某个bucket下的文件字节异常",e);thrownewJdCloudFileServiceException(e);}finally{IoUtil.close(objectContent);}}@OverridepublicvoidsetFileAcl(StringbucketName,Stringkey,BucketAuthEnumbucketAuthEnum){try{if(bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)){ossClient.setObjectAcl(bucketName,key,CannedAccessControlList.Private);}elseif(bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)){ossClient.setObjectAcl(bucketName,key,CannedAccessControlList.PublicRead);}elseif(bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)){ossClient.setObjectAcl(bucketName,key,CannedAccessControlList.PublicReadWrite);}}catch(Exceptione){log.error("JdCloud-oss-文件访问权限管理异常",e);thrownewJdCloudFileServiceException(e);}}@OverridepublicvoidcopyFile(StringoriginBucketName,StringoriginFileKey,StringnewBucketName,StringnewFileKey){try{ossClient.copyObject(originBucketName,originFileKey,newBucketName,newFileKey);}catch(Exceptione){log.error("JdCloud-oss-拷贝文件异常",e);thrownewJdCloudFileServiceException(e);}}@OverridepublicStringgetFileAuthUrl(StringbucketName,Stringkey,LongtimeoutMillis){try{Dateexpiration=newDate(System.currentTimeMillis()+timeoutMillis);URLurl=ossClient.generatePresignedUrl(bucketName,key,expiration);returnurl.toString();}catch(Exceptione){log.error("JdCloud-oss-获取文件的下载地址异常",e);thrownewJdCloudFileServiceException(e);}}@OverridepublicvoiddeleteFile(StringbucketName,Stringkey){try{ossClient.deleteObject(bucketName,key);}catch(Exceptione){log.error("JdCloud-oss-删除文件异常",e);thrownewJdCloudFileServiceException(e);}}/***初始化分片文件上传**@parambucketName文件桶*@paramkey文件key*@return本次文件上传唯一标识*/@OverridepublicStringinitiateMultipartUpload(StringbucketName,Stringkey){InitiateMultipartUploadRequestrequest=newInitiateMultipartUploadRequest(bucketName,key);InitiateMultipartUploadResultinitiateMultipartUploadResult=ossClient.initiateMultipartUpload(request);log.info("京东云初始化分片文件上传:{}",key);returninitiateMultipartUploadResult.getUploadId();}/***上传分片文件**@paramfileUploadPart分片文件参数*@return上传结果*/@OverridepublicFileUploadPartResultuploadPart(FileUploadPartfileUploadPart){UploadPartRequestrequest=JdCloudConvert.INSTANCE.convert(fileUploadPart);UploadPartResultuploadPartResult=ossClient.uploadPart(request);FileUploadPartResultresult=JdCloudConvert.INSTANCE.convert(uploadPartResult.getPartETag());result.setPartSize(fileUploadPart.getPartSize());log.info("京东云分片文件上传:{},结果:{}",fileUploadPart,request);returnresult;}/***完成分片上传**@paramcompleteFileUploadPart请求对象*@return结果信息*/@OverridepublicCompleteFileUploadPartResultcompleteMultipartUpload(CompleteFileUploadPartcompleteFileUploadPart){CompleteMultipartUploadRequestrequest=JdCloudConvert.INSTANCE.convert(completeFileUploadPart);CompleteMultipartUploadResultresult=ossClient.completeMultipartUpload(request);log.info("京东云合并文件:{},结果:{}",completeFileUploadPart,result);returnJdCloudConvert.INSTANCE.convert(result);}/***取消文件分片上传**@parambucketName文件桶*@paramobjectName对象key*@paramuploadId上传ID*@return*/@OverridepublicvoidabortMultipartUpload(StringbucketName,StringobjectName,StringuploadId){ossClient.abortMultipartUpload(newAbortMultipartUploadRequest(bucketName,objectName,uploadId));}}

腾讯云对象存储分片上传

packagecom.aimilin.common.base.file.modular.tencent;importcn.hutool.core.io.IoUtil;importcom.aimilin.common.base.file.FilePartOperator;importcom.aimilin.common.base.file.common.enums.FileLocationEnum;importcom.aimilin.common.base.file.modular.aliyun.prop.AliyunConvert;importcom.aimilin.common.base.file.modular.tencent.prop.TenConvert;importcom.aimilin.common.base.file.param.CompleteFileUploadPart;importcom.aimilin.common.base.file.param.CompleteFileUploadPartResult;importcom.aimilin.common.base.file.param.FileUploadPart;importcom.aimilin.common.base.file.param.FileUploadPartResult;importcom.qcloud.cos.COSClient;importcom.qcloud.cos.ClientConfig;importcom.qcloud.cos.auth.BasicCOSCredentials;importcom.qcloud.cos.auth.COSCredentials;importcom.qcloud.cos.exception.CosClientException;importcom.qcloud.cos.exception.CosServiceException;importcom.qcloud.cos.http.HttpMethodName;importcom.qcloud.cos.model.*;importcom.qcloud.cos.region.Region;importcom.qcloud.cos.transfer.TransferManager;importcom.qcloud.cos.transfer.TransferManagerConfiguration;importcom.aimilin.common.base.file.FileOperator;importcom.aimilin.common.base.file.common.enums.BucketAuthEnum;importcom.aimilin.common.base.file.modular.tencent.exp.TencentFileServiceException;importcom.aimilin.common.base.file.modular.tencent.prop.TenCosProperties;importlombok.extern.slf4j.Slf4j;importjavax.activation.MimetypesFileTypeMap;importjava.io.ByteArrayInputStream;importjava.io.InputStream;importjava.net.URL;importjava.util.ArrayList;importjava.util.Date;importjava.util.List;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;/***腾讯云内网文件操作**/@Slf4jpublicclassTenFileOperatorimplementsFilePartOperator{@OverridepublicFileLocationEnumgetFileLocation(){returnFileLocationEnum.TENCENT;}privatefinalTenCosPropertiestenCosProperties;privateCOSClientcosClient;privateTransferManagertransferManager;publicTenFileOperator(TenCosPropertiestenCosProperties){this.tenCosProperties=tenCosProperties;initClient();}@OverridepublicvoidinitClient(){//1.初始化用户身份信息StringsecretId=tenCosProperties.getSecretId();StringsecretKey=tenCosProperties.getSecretKey();COSCredentialscred=newBasicCOSCredentials(secretId,secretKey);//2.设置bucket的区域,COS地域的简称请参照https://cloud.tencent.com/document/product/436/6224Regionregion=newRegion(tenCosProperties.getRegionId());ClientConfigclientConfig=newClientConfig(region);//3.生成cos客户端。cosClient=newCOSClient(cred,clientConfig);//4.线程池大小,建议在客户端与COS网络充足(例如使用腾讯云的CVM,同地域上传COS)的情况下,设置成16或32即可,可较充分的利用网络资源//对于使用公网传输且网络带宽质量不高的情况,建议减小该值,避免因网速过慢,造成请求超时。ExecutorServicethreadPool=Executors.newFixedThreadPool(32);//5.传入一个threadpool,若不传入线程池,默认TransferManager中会生成一个单线程的线程池。transferManager=newTransferManager(cosClient,threadPool);//6.设置高级接口的分块上传阈值和分块大小为10MBTransferManagerConfigurationtransferManagerConfiguration=newTransferManagerConfiguration();transferManagerConfiguration.setMultipartUploadThreshold(10*1024*1024);transferManagerConfiguration.setMinimumUploadPartSize(10*1024*1024);transferManager.setConfiguration(transferManagerConfiguration);}@OverridepublicvoiddestroyClient(){cosClient.shutdown();}@OverridepublicObjectgetClient(){returncosClient;}@OverridepublicbooleandoesBucketExist(StringbucketName){try{returncosClient.doesBucketExist(bucketName);}catch(CosServiceExceptione){thrownewTencentFileServiceException(e);}catch(CosClientExceptione){thrownewTencentFileServiceException(e);}}@OverridepublicvoidsetBucketAcl(StringbucketName,BucketAuthEnumbucketAuthEnum){try{if(bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)){cosClient.setBucketAcl(bucketName,CannedAccessControlList.Private);}elseif(bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)){cosClient.setBucketAcl(bucketName,CannedAccessControlList.PublicRead);}elseif(bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)){cosClient.setBucketAcl(bucketName,CannedAccessControlList.PublicReadWrite);}}catch(CosServiceExceptione){thrownewTencentFileServiceException(e);}catch(CosClientExceptione){thrownewTencentFileServiceException(e);}}@OverridepublicbooleanisExistingFile(StringbucketName,Stringkey){try{cosClient.getObjectMetadata(bucketName,key);returntrue;}catch(CosServiceExceptione){returnfalse;}}@OverridepublicvoidstorageFile(StringbucketName,Stringkey,byte[]bytes){//根据文件名获取contentTypeStringcontentType="application/octet-stream";if(key.contains(".")){contentType=MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(key);}//上传文件ByteArrayInputStreambyteArrayInputStream=null;try{byteArrayInputStream=newByteArrayInputStream(bytes);ObjectMetadataobjectMetadata=newObjectMetadata();objectMetadata.setContentType(contentType);cosClient.putObject(bucketName,key,newByteArrayInputStream(bytes),objectMetadata);}catch(CosServiceExceptione){thrownewTencentFileServiceException(e);}catch(CosClientExceptione){thrownewTencentFileServiceException(e);}finally{IoUtil.close(byteArrayInputStream);}}@OverridepublicvoidstorageFile(StringbucketName,Stringkey,InputStreaminputStream,longfileSize){//根据文件名获取contentTypeStringcontentType="application/octet-stream";if(key.contains(".")){contentType=MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(key);}//上传文件try{ObjectMetadataobjectMetadata=newObjectMetadata();objectMetadata.setContentType(contentType);objectMetadata.setContentLength(fileSize);cosClient.putObject(bucketName,key,inputStream,objectMetadata);}catch(CosServiceExceptione){thrownewTencentFileServiceException(e);}catch(CosClientExceptione){thrownewTencentFileServiceException(e);}finally{IoUtil.close(inputStream);}}@Overridepublicbyte[]getFileBytes(StringbucketName,Stringkey){COSObjectInputStreamcosObjectInput=null;try{GetObjectRequestgetObjectRequest=newGetObjectRequest(bucketName,key);COSObjectcosObject=cosClient.getObject(getObjectRequest);cosObjectInput=cosObject.getObjectContent();returnIoUtil.readBytes(cosObjectInput);}catch(CosServiceExceptione){thrownewTencentFileServiceException(e);}catch(CosClientExceptione){thrownewTencentFileServiceException(e);}finally{IoUtil.close(cosObjectInput);}}@OverridepublicvoidsetFileAcl(StringbucketName,Stringkey,BucketAuthEnumbucketAuthEnum){if(bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)){cosClient.setObjectAcl(bucketName,key,CannedAccessControlList.Private);}elseif(bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)){cosClient.setObjectAcl(bucketName,key,CannedAccessControlList.PublicRead);}elseif(bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)){cosClient.setObjectAcl(bucketName,key,CannedAccessControlList.PublicReadWrite);}}@OverridepublicvoidcopyFile(StringoriginBucketName,StringoriginFileKey,StringnewBucketName,StringnewFileKey){//初始化拷贝参数RegionsrcBucketRegion=newRegion(tenCosProperties.getRegionId());CopyObjectRequestcopyObjectRequest=newCopyObjectRequest(srcBucketRegion,originBucketName,originFileKey,newBucketName,newFileKey);//拷贝对象try{transferManager.copy(copyObjectRequest,cosClient,null);}catch(CosServiceExceptione){thrownewTencentFileServiceException(e);}catch(CosClientExceptione){thrownewTencentFileServiceException(e);}}@OverridepublicStringgetFileAuthUrl(StringbucketName,Stringkey,LongtimeoutMillis){GeneratePresignedUrlRequestpresignedUrlRequest=newGeneratePresignedUrlRequest(bucketName,key,HttpMethodName.GET);DateexpirationDate=newDate(System.currentTimeMillis()+timeoutMillis);presignedUrlRequest.setExpiration(expirationDate);URLurl=null;try{url=cosClient.generatePresignedUrl(presignedUrlRequest);}catch(CosServiceExceptione){thrownewTencentFileServiceException(e);}catch(CosClientExceptione){thrownewTencentFileServiceException(e);}returnurl.toString();}@OverridepublicvoiddeleteFile(StringbucketName,Stringkey){cosClient.deleteObject(bucketName,key);}/***初始化分片文件上传**@parambucketName文件桶*@paramkey文件key*@return本次文件上传唯一标识*/@OverridepublicStringinitiateMultipartUpload(StringbucketName,Stringkey){InitiateMultipartUploadRequestrequest=newInitiateMultipartUploadRequest(bucketName,key);InitiateMultipartUploadResultresult=cosClient.initiateMultipartUpload(request);log.info("腾讯云初始化分片文件上传:{}",key);returnresult.getUploadId();}/***上传分片文件**@paramfileUploadPart分片文件参数*@return上传结果*/@OverridepublicFileUploadPartResultuploadPart(FileUploadPartfileUploadPart){UploadPartRequestrequest=TenConvert.INSTANCE.convert(fileUploadPart);UploadPartResultresult=cosClient.uploadPart(request);FileUploadPartResultconvert=TenConvert.INSTANCE.convert(result);convert.setPartSize(fileUploadPart.getPartSize());log.info("腾讯云分片文件上传:{},结果:{}",fileUploadPart,request);returnconvert;}/***完成分片上传**@paramcompleteFileUploadPart请求对象*@return结果信息*/@OverridepublicCompleteFileUploadPartResultcompleteMultipartUpload(CompleteFileUploadPartcompleteFileUploadPart){List<PartETag>tags=newArrayList<>();for(FileUploadPartResultpartETag:completeFileUploadPart.getPartETags()){tags.add(newPartETag(partETag.getPartNumber(),partETag.getPartETag()));}CompleteMultipartUploadRequestrequest=newCompleteMultipartUploadRequest(completeFileUploadPart.getBucketName(),completeFileUploadPart.getObjectName(),completeFileUploadPart.getUploadId(),tags);CompleteMultipartUploadResultresult=cosClient.completeMultipartUpload(request);log.info("京东云合并文件:{},结果:{}",completeFileUploadPart,result);returnTenConvert.INSTANCE.convert(result);}/***取消文件分片上传**@parambucketName文件桶*@paramobjectName对象key*@paramuploadId上传ID*@return*/@OverridepublicvoidabortMultipartUpload(StringbucketName,StringobjectName,StringuploadId){AbortMultipartUploadRequestrequest=newAbortMultipartUploadRequest(bucketName,objectName,uploadId);cosClient.abortMultipartUpload(request);}}

分片上传前端代码实现

<template><divclass="upload__wrap":class="`upload__wrap--${size}`"><divclass="files"v-for="imginexistsImgs":key="img.id"><templatev-if="pictureType.includes(handleType(img.fileSuffix))"><!--图片类型--><imgwidth="104"height="104":src="handleImg(img.id,208,208)"/><divclass="btn__wraps"><divclass="btn__innerwraps"><a-iconclass="icon__btn"type="eye"@click="$refs.previewForm.preview({id:img.id})"/><a-popconfirmplacement="topRight"title="确认删除?"@confirm="()=>deleteImg(img.id)"><a-iconclass="icon__btn"type="delete"/></a-popconfirm></div></div></template><templatev-else-if="threedType.includes(handleType(img.fileSuffix))"><imgwidth="104"height="104"src="https://qixn-bj.oss-cn-beijing.aliyuncs.com/seosjz/uploadfile/all/png/uso0mcizd4m.png"/><divclass="btn__wraps"><divclass="btn__innerwraps"><a-iconclass="icon__btn"type="eye"@click="show3dModal(img)"/><a-popconfirmplacement="topRight"title="确认删除?"@confirm="()=>deleteImg(img.id)"><a-iconclass="icon__btn"type="delete"/></a-popconfirm></div></div></template><templatev-else>当前类型文件暂不支持预览</template></div><divclass="tempimg__placeholder"v-for="tempintempImgArr":key="temp.uid">上传中…</div><a-uploadname="upload":list-type="listType":file-list="fileList":accept="format":multiple="multiple":before-upload="beforeUpload":customRequest="customRequest"><divv-if="existsImgs.length+tempImgArr.length<maxPicsLength"><a-icontype="plus"/><divclass="ant-upload-text">上传</div></div></a-upload><preview-formref="previewForm"></preview-form><preview3d-model:is3dModelShow="is3dModelShow":carousel-lists="preview3dModel"title="3D模型预览"@closeModal="closeModal"></preview3d-model><!--:carousel-lists=""--></div></template><script>//import{sysFileInfoPage,sysFileInfoDelete,sysFileInfoPartUpload,sysFileInfoDownload}from'@/api/modular/system/fileManage'import{sysFileInfoPartUpload}from'@/api/modular/system/fileManage'importpreviewFormfrom'@/views/system/file/previewForm.vue'importPreview3dModelfrom'@/views/system/file/preview3dmodel.vue'import{handleImg}from'@/utils/util'importSparkMD5from'spark-md5'import{SUCCESS,SERVICE_ERROR,UPLOADING}from'@/assets/js/responseCode'constSIZEUNIT=1*1024*1024exportdefault{components:{previewForm,Preview3dModel},props:{isCloseUpload:{type:Boolean,default:false},size:{type:String,default:'default'},format:{type:String,default:'image/gif,image/jpeg,image/png,image/jpg'},listType:{type:String,default:'picture-card'},maxPicsLength:{type:Number,default:9},uploadText:{type:String,default:'上传'},existsImgs:{type:Array,default(){return[]}},maxSize:{type:Number,default:20},multiple:{type:Boolean,default:false}},data(){return{pictureType:['.gif','.jpeg','.png','.jpg'],threedType:['.json','.obj','.dae','.ply','.gltf','.stl','.fbx'],previewVisible:false,previewImage:'',fileList:[],//loading:false,is3dModelShow:false,preview3dModel:[],tempImgArr:[],isStopUpload:false}},create(){this.timer=nullconsole.log('this',this)},watch:{isCloseUpload:{handler(newval){if(newval){this.$set(this,'tempImgArr',[])this.$emit('imgUploadingStatus',0)}},immediate:true}},methods:{handleImg,show3dModal(obj){this.preview3dModel=[obj]this.is3dModelShow=true},closeModal(){this.is3dModelShow=false},handleType(filetType){returnfiletType.indexOf('.')>-1?filetType:'.'+filetType},beforeUpload(file,fileList){console.log('this',this)returnnewPromise((resolve,reject)=>{lettype=file.typeif(!type){type='.'+file.name.split('.').pop()}constisFormatFiles=this.format.replace(/\s*/g,'').split(',').includes(type)if(!isFormatFiles){this.$message.error(`只支持以下${this.format}格式!`)returnreject(newError(`只支持以下${this.format}格式!`))}constmaxSizeLimit=this.threedType.includes(type)?100:20constisLtMaxSize=file.size/SIZEUNIT<maxSizeLimitif(!isLtMaxSize){this.$message.error(`图片须小于${maxSizeLimit}MB!`)returnreject(newError(`图片须小于${maxSizeLimit}MB!`))}//是否上传图片超过最大限度if(this.existsImgs.length+this.tempImgArr.length>=this.maxPicsLength){if(this.timer){clearTimeout(this.timer)}this.timer=setTimeout(()=>{this.$message.error(`最多只能上传${this.maxPicsLength}张!`)},300)returnreject(newError(`最多只能上传${this.maxPicsLength}张!`))}this.isStopUpload=false//this.loading=truethis.$set(this,'tempImgArr',[...this.tempImgArr,file.uid])this.$emit('imgUploadingStatus',[...this.tempImgArr,file.uid].length)this.$emit('resetUploadStatus')resolve(true)})//returnisFormatFiles&&isLt2M},preview(id){this.$refs.previewForm.preview({id})},deleteImg(id){this.$emit('deletePic',id)},/***上传文件*/customRequest(data){constfileType='.'+data.file.name.split('.').pop()constfileReader=newFileReader()constblobSlice=File.prototype.slice||File.prototype.mozSlice||File.prototype.webkitSliceletcurrentChunk=0constchunkSize=4*1024*1024constchunks=Math.ceil(data.file.size/chunkSize)constspark=newSparkMD5.ArrayBuffer()constpartChunksArr=[]constfileData={}loadNext()fileReader.onload=e=>{spark.append(e.target.result)constsparkChunk=newSparkMD5.ArrayBuffer()sparkChunk.append(e.target.result)constpartMd5=sparkChunk.end()partChunksArr.push({file:fileData[currentChunk],partNumber:currentChunk+1,partMd5,partSize:chunkSize,totalSize:data.file.size})currentChunk++if(currentChunk<chunks){loadNext()}else{constmd5=spark.end()this.finalUploadFn(partChunksArr,fileType,data,md5)}}fileReader.onerror=function(){this.$message.error(`文件${data.file.name}读取出错,请检查该文件`)//data.cancel()}functionloadNext(){conststart=currentChunk*chunkSizeconstend=((start+chunkSize)>=data.file.size)?data.file.size:start+chunkSizeconstcurrentChunkData=blobSlice.call(data.file,start,end)fileReader.readAsArrayBuffer(currentChunkData)fileData[currentChunk]=currentChunkData}},finalUploadFn(formData,fileType,data,wholeFileMd5){formData.forEach(item=>{constnewFormData=newFormData()//newFormData.set('file',data.file)newFormData.set('uid',data.file.uid)newFormData.set('filename',data.file.name)Object.keys(item).forEach(key=>{newFormData.set(key,item[key])newFormData.set('fileMd5',wholeFileMd5)})if(this.isStopUpload){return}sysFileInfoPartUpload(newFormData).then((res)=>{//this.loading=falseif(res.code===SUCCESS&&res.data?.fileState===SUCCESS){this.$emit('getNewPics',{id:res.data.fileId,fileSuffix:fileType})constnewTempImgArr=this.tempImgArr.filter(item=>item!==res.data?.uid)this.$set(this,'tempImgArr',newTempImgArr)this.$emit('imgUploadingStatus',newTempImgArr.length)//this.$refs.table.refresh()}elseif(res.code===SUCCESS&&res.data?.fileState===UPLOADING){}elseif(res.code===SUCCESS&&res.data?.fileState===SERVICE_ERROR){if(!this.failupload){this.failupload={}this.failupload[data.file.uid]=data.file.uidsysFileInfoPartUpload(newFormData)}else{if(!this.failupload[data.file.uid]){sysFileInfoPartUpload(newFormData)this.failupload[data.file.uid]=data.file.uid}}}elseif(res.code!==SUCCESS){//上传失败,从占位图中移除一个constnewTempImgArr=this.tempImgArrnewTempImgArr.pop()this.$set(this,'tempImgArr',newTempImgArr)this.$emit('imgUploadingStatus',newTempImgArr.length)if(this.timer){clearTimeout(this.timer)}this.timer=setTimeout(()=>{this.$message.error('上传失败!'+res.message)},300)}}).catch(e=>{constnewTempImgArr=this.tempImgArrnewTempImgArr.pop()this.$set(this,'tempImgArr',newTempImgArr)this.$emit('imgUploadingStatus',newTempImgArr.length)console.log('error',e)//this.loading=false//this.tempImgArr.length&&this.$message.error('上传失败,请重新上传')}).finally((p)=>{console.log('sysFileInfoPartUpload',p)//this.loading=false})})},clearTimer(){clearTimeout(this.timer)this.$set(this,'tempImgArr',[])this.$emit('imgUploadingStatus',0)this.isStopUpload=true}},beforeDestoryed(){this.clearTimer()}}</script><style>/*youcanmakeupuploadbuttonandsamplestylebyusingstylesheets*/.ant-upload-select-picture-cardi{font-size:32px;color:#999;}.ant-upload-select-picture-card.ant-upload-text{margin-top:8px;color:#666;}</style><stylelang="less"scoped>.upload__wrap{display:-webkit-inline-box;display:-moz-inline-box;display:inline-box;flex-wrap:wrap;.files{position:relative;width:104px;height:104px;margin-right:10px;margin-bottom:10px;.btn__wraps{position:absolute;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,0);display:flex;align-items:center;justify-content:center;transition:all0.3slinear;z-index:-1;.btn__innerwraps{display:flex;.icon__btn{margin-right:10px;font-size:16px;color:rgba(255,255,255,0);cursor:pointer;&:last-child{margin-right:0;}}}}&:hover{.btn__wraps{background:rgba(0,0,0,0.5);transition:all0.3slinear;z-index:1;.btn__innerwraps{.icon__btn{color:rgba(255,255,255,0.8);}}}}}.tempimg__placeholder{width:104px;height:104px;display:flex;justify-content:center;align-items:center;border:1pxsolid#d9d9d9;margin-right:10px;margin-bottom:10px;}}</style>
 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:Java怎么将大文件分片上传的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:jquery的伪类选择器怎么使用下一篇:

9 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18