好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Java超详细大文件分片上传代码

Java 大文件分片上传

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

首先是交互的控制器

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

package com.aimilin.component.system.service.modular.file.controller;

import com.aimilin.common.core.pojo.base.param.BaseParam;

import com.aimilin.common.core.pojo.response.ResponseData;

import com.aimilin.common.log.annotation.BusinessLog;

import com.aimilin.common.log.enums.LogOpTypeEnum;

import com.aimilin.common.security.annotation.Permission;

import com.aimilin.component.system.service.modular.file.param.SysPartFileParam;

import com.aimilin.component.system.service.modular.file.result.SysPartFileResult;

import com.aimilin.component.system.service.modular.file.service.SysPartFileService;

import lombok.extern.slf4j.Slf4j;

import org.springframework.validation.annotation.Validated;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**

  * 系统大文件上传

  *

  * @version V1.0

  * @date 2022/5/24 11:22

  */

@Slf4j

@RestController

public class SysPartFileController {

     @Resource

     private SysPartFileService sysPartFileService;

     /**

      * 上传大文件

      *

      */

     @Permission

     @PostMapping ( "/sysFileInfo/partUpload" )

     public ResponseData<SysPartFileResult> partUpload( @Validated (BaseParam.add. class ) SysPartFileParam partFile) {

         return ResponseData.success(sysPartFileService.partUpload(partFile));

     }

     /**

      * 获取文件上传状态

      *

      */

     @Permission

     @GetMapping ( "/sysFileInfo/partUpload/status" )

     public ResponseData<SysPartFileResult> getPartUploadStatus( @Validated (BaseParam.detail. class ) SysPartFileParam partFile) {

         return ResponseData.success(sysPartFileService.getPartUploadStatus(partFile));

     }

     /**

      * 获取文件上传状态

      *

      */

     @Permission

     @GetMapping ( "/sysFileInfo/partUpload/cancel" )

     @BusinessLog (title = "文件_上传大文件_取消" , opType = LogOpTypeEnum.OTHER)

     public ResponseData<SysPartFileResult> cancelUpload( @Validated (BaseParam.detail. class ) SysPartFileParam partFile) {

         return ResponseData.success(sysPartFileService.cancelUpload(partFile));

     }

}

上传文件分片参数接收

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

package com.aimilin.component.system.service.modular.file.param;

import java.io.Serializable;

import java.util.Objects;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;

import com.aimilin.common.core.pojo.base.param.BaseParam;

import lombok.Getter;

import lombok.Setter;

import lombok.ToString;

import org.springframework.web.multipart.MultipartFile;

import javax.validation.constraints.NotNull;

/**

  * 大文件断点续传

  *

  * @version V1.0

  * @date 2022/5/24 10:52

  */

@Getter

@Setter

@ToString

public class SysPartFileParam extends BaseParam implements Serializable {

     /**

      * 文件上传Id, 前端传入的值

      */

     @NotNull (message = "uid不能为空" , groups = {BaseParam.detail. class , BaseParam.add. class })

     private String uid;

     /**

      * 上传文件名称

      */

     private String filename;

     /**

      * 当前文件块,从1开始

      */

     @NotNull (message = "partNumber不能为空" , groups = {BaseParam.add. class })

     private Integer partNumber;

     /**

      * 当前分块Md5

      */

     @NotNull (message = "partMd5不能为空" , groups = {BaseParam.add. class })

     private String partMd5;

     /**

      * 分块大小,根据 totalSize 和这个值你就可以计算出总共的块数。注意最后一块的大小可能会比这个要大。

      */

     @NotNull (message = "partSize不能为空" , groups = {BaseParam.add. class })

     private Long partSize;

     /**

      * 总大小

      */

     @NotNull (message = "totalSize不能为空" , groups = {BaseParam.add. class })

     private Long totalSize;

     /**

      * 文件标识,MD5指纹

      */

     @NotNull (message = "fileMd5不能为空" , groups = {BaseParam.add. class })

     private String fileMd5;

     /**

      * 二进制文件

      */

     @NotNull (message = "file不能为空" , groups = {BaseParam.add. class })

     private MultipartFile file;

     /**

      * 总块数, (int)totalSize / partSize 最后一个模块要大一点;

      *

      * @return 结果

      */

     public Integer getTotalParts() {

         if (Objects.isNull(totalSize) || Objects.isNull(partSize)) {

             return 0 ;

         }

         return new Double(Math.ceil(totalSize * 1.0 / partSize)).intValue();

     }

     public String getFilename() {

         if (StringUtils.isBlank( this .filename) && Objects.isNull( this .file)) {

             return null ;

         }

         return StringUtils.isBlank( this .filename) ? this .file.getOriginalFilename() : this .filename;

     }

}

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

/**

  * 参数校验分组:分页

  */

public @interface page {

}

/**

  * 参数校验分组:列表

  */

public @interface list {

}

/**

  * 参数校验分组:下拉

  */

public @interface dropDown {

}

/**

  * 参数校验分组:增加

  */

public @interface add {

}

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

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

package com.aimilin.component.system.service.modular.file.service;

import com.aimilin.component.system.service.modular.file.param.SysPartFileParam;

import com.aimilin.component.system.service.modular.file.result.SysPartFileResult;

/**

  * 块文件上传

  *

  * @version V1.0

  * @date 2022/5/24 10:59

  */

public interface SysPartFileService {

     /**

      * 文件块上传公共前缀

      */

     public static final String PART_FILE_KEY = "PART_FILE" ;

     /**

      * 文件块上传

      * 1. 将上传文件按照partSize拆分成多个文件块

      * 2. 判断当前文件块是否已经上传

      * 3. 未上传,则上传当前文本块

      * 4. 已上传则不处理

      * 5. 统计当前文本块上传进度信息

      * 6. 判断所有文本块是否已经上传完成,如果上传完成则触发文件合并

      */

     public SysPartFileResult partUpload(SysPartFileParam partFile);

     /**

      * 获取文件上传状态

      *

      * @param partFile 上传文件信息

      * @return 文件上传状态结果

      */

     public SysPartFileResult getPartUploadStatus(SysPartFileParam partFile);

     /**

      * 取消文件上传

      *

      * @param partFile 上传文件信息

      * @return 文件上传状态结果

      */

     public SysPartFileResult cancelUpload(SysPartFileParam partFile);

}

服务实现类:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

package com.aimilin.component.system.service.modular.file.service.impl;

import cn.hutool.core.io.FileUtil;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;

import com.aimilin.common.base.file.FilePartOperator;

import com.aimilin.common.base.file.param.AbortMultipartUploadResult;

import com.aimilin.common.base.file.param.CompleteFileUploadPart;

import com.aimilin.common.base.file.param.FileUploadPart;

import com.aimilin.common.base.file.param.FileUploadPartResult;

import com.aimilin.common.cache.RedisService;

import com.aimilin.common.core.consts.CommonConstant;

import com.aimilin.common.core.context.login.LoginContextHolder;

import com.aimilin.common.core.exception.ServiceException;

import com.aimilin.component.system.service.modular.file.convert.SysPartFileConvert;

import com.aimilin.component.system.service.modular.file.entity.SysFileInfo;

import com.aimilin.component.system.service.modular.file.enums.SysFileInfoExceptionEnum;

import com.aimilin.component.system.service.modular.file.enums.SysPartFileEnum;

import com.aimilin.component.system.service.modular.file.param.SysPartFileParam;

import com.aimilin.component.system.service.modular.file.result.SysPartFileCache;

import com.aimilin.component.system.service.modular.file.result.SysPartFileCache.FileInfo;

import com.aimilin.component.system.service.modular.file.result.SysPartFileCache.SysFilePart;

import com.aimilin.component.system.service.modular.file.result.SysPartFileResult;

import com.aimilin.component.system.service.modular.file.service.SysFileInfoService;

import com.aimilin.component.system.service.modular.file.service.SysPartFileService;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.io.FilenameUtils;

import org.redisson.api.RLock;

import org.redisson.api.RedissonClient;

import org.springframework.stereotype.Service;

import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;

import java.io.IOException;

import java.util.*;

import java.util.concurrent.TimeUnit;

import static com.aimilin.component.system.service.config.FileConfig.DEFAULT_BUCKET;

/**

  * 大文件上传功能服务实现

  *

  * @version V1.0

  * @date 2022/5/24 11:53

  */

@Slf4j

@Service

public class SysPartFileServiceImpl implements SysPartFileService {

     @Resource

     private FilePartOperator fileOperator;

     @Resource

     private RedisService redisService;

     @Resource

     private SysFileInfoService sysFileInfoService;

     @Resource

     private RedissonClient redisson;

     /**

      * 文件块上传

      * 1. 将上传文件按照partSize拆分成多个文件块

      * 2. 判断当前文件块是否已经上传

      * 3. 未上传,则上传当前文本块

      * 4. 已上传则不处理

      * 5. 统计当前文本块上传进度信息

      * 6. 判断所有文本块是否已经上传完成,如果上传完成则触发文件合并

      *

      * @param partFile 上传文件

      * @return SysPartFileResult 文件上传结果

      */

     @Override

     public SysPartFileResult partUpload(SysPartFileParam partFile) {

         MultipartFile file = partFile.getFile();

         log.info( "分块上传文件:{}, partNumber:{}/{}, partSize:{}/{}" ,

                 partFile.getFilename(), partFile.getPartNumber(), partFile.getTotalParts(), file.getSize(), partFile.getPartSize());

         SysPartFileResult partUploadStatus = this .getPartUploadStatus(partFile);

         // 已经上传该部分则直接返回当前文件状态

         if (SysPartFileEnum.SUCCESS.getCode().equals(partUploadStatus.getPartState())) {

             return partUploadStatus;

         }

         // 上传分片文件

         FileUploadPart fileUploadPart = this .getFileUploadPart(partFile);

         try {

             FileUploadPartResult uploadPartResult = fileOperator.uploadPart(fileUploadPart);

             this .setPartUploadStatus(partFile, uploadPartResult);

         } catch (Exception e) {

             log.error( "文件分片上传失败,请求:{}:{}" , partFile, e.getMessage(), e);

             throw new ServiceException(SysFileInfoExceptionEnum.FILE_OSS_ERROR);

         }

         return this .getPartUploadStatus(partFile);

     }

     /**

      * 获取文件上传状态

      *

      * @param partFile 上传文件信息

      * @return 文件上传状态结果

      */

     @Override

     public SysPartFileResult getPartUploadStatus(SysPartFileParam partFile) {

         SysPartFileCache fileCache = redisService.getCacheObject(getPartFileKey(partFile.getUid()));

         SysPartFileResult result;

         // 如果没有上传过则返回默认值

         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()));

         }

         return result;

     }

     /**

      * 取消文件上传

      *

      * @param partFile 上传文件信息

      * @return 文件上传状态结果

      */

     @Override

     public SysPartFileResult cancelUpload(SysPartFileParam partFile) {

         String cacheKey = getPartFileKey(partFile.getUid());

         SysPartFileCache fileCache = redisService.getCacheObject(cacheKey);

         if (Objects.isNull(fileCache)) {

             throw new ServiceException(SysFileInfoExceptionEnum.NOT_EXISTED_FILE);

         }

         SysPartFileCache.FileInfo fileInfo = fileCache.getFileInfo();

         fileOperator.abortMultipartUpload(fileInfo.getBucketName(), fileInfo.getObjectName(), fileInfo.getUploadId());

         log.info( "取消文件上传:{}" , partFile.getUid());

         SysPartFileResult sysPartFileResult = SysPartFileConvert.INSTANCE.toSysPartFileResult(partFile);

         sysPartFileResult.setFileState(SysPartFileEnum.CANCELED.getCode());

         redisService.deleteObject(cacheKey);

         return sysPartFileResult;

     }

     /**

      * 文件分片上传,设置文件分片信息

      *

      * @param partFile         分片文件参数

      * @param uploadPartResult 文件上传结果信息

      */

     private void setPartUploadStatus(SysPartFileParam partFile, FileUploadPartResult uploadPartResult) {

         String redisKey = getPartFileKey(partFile.getUid());

         if (!redisService.hasKey(redisKey)) {

             throw new ServiceException(SysFileInfoExceptionEnum.FILE_CACHE_ERROR);

         }

         RLock lock = redisson.getLock(CommonConstant.getLockKey(redisKey));

         try {

             lock.lock();

             SysPartFileCache fileCache = redisService.getCacheObject(redisKey);

             Set<SysFilePart> filePartList = fileCache.getFilePartList();

             if (Objects.isNull(filePartList)) {

                 filePartList = new HashSet<>();

                 fileCache.setFilePartList(filePartList);

             }

             SysFilePart sysFilePart = new SysFilePart();

             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 ( new HashSet<>(fileCache.getUploadedParts()).size() == fileCache.getTotalParts()) {

                 CompleteFileUploadPart completeFileUploadPart = 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 (Exception e) {

             log.error( "设置文件分片上传状态异常,{},上传结果:{}" , partFile, uploadPartResult, e);

             throw new ServiceException(SysFileInfoExceptionEnum.PART_FILE_SET_STATE_ERROR);

         } finally {

             lock.unlock();

         }

     }

     /**

      * 保存文件信息到 数据库

      *

      * @param partFile  分片文件

      * @param fileCache 文件缓存对象

      */

     private void saveFileInfo(SysPartFileParam partFile, SysPartFileCache fileCache) {

         SysFileInfo sysFileInfo = new SysFileInfo();

         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());

         boolean save = sysFileInfoService.save(sysFileInfo);

         log.info( "保存文件信息完成:{},结果:{}" , partFile.getFilename(), save);

     }

     /**

      * 获取文件上传分片信息

      *

      * @param partFile 分片文件参数

      * @return 需要上传的分片文件信息

      */

     private FileUploadPart getFileUploadPart(SysPartFileParam partFile) {

         try {

             SysPartFileCache fileCache = redisService.getCacheObject(getPartFileKey(partFile.getUid()));

             if (Objects.isNull(fileCache)) {

                 fileCache = this .initSysPartFileCache(partFile);

             }

             return SysPartFileConvert.INSTANCE.toFileUploadPart(fileCache.getFileInfo(), partFile);

         } catch (IOException e) {

             log.error( "获取文件分片对象异常:{}" , e.getMessage(), e);

             throw new ServiceException(SysFileInfoExceptionEnum.FILE_STREAM_ERROR);

         }

     }

     /**

      * 初始化文件缓存对象,进入该方法说明缓存为空

      * @param partFile 分片文件

      */

     private SysPartFileCache initSysPartFileCache(SysPartFileParam partFile) {

         String key  =  getPartFileKey(partFile.getUid());

         RLock lock = redisson.getLock(CommonConstant.getLockKey(key));

         try {

             lock.lock();

             SysPartFileCache fileCache = redisService.getCacheObject(key);

             if (Objects.isNull(fileCache)){

                 Long fileId = IdWorker.getId();

                 String objectName = SysFileUtils.getFileObjectName(partFile.getFilename(), fileId);

                 String uploadId = fileOperator.initiateMultipartUpload(DEFAULT_BUCKET, objectName);

                 fileCache = SysPartFileConvert.INSTANCE.toSysPartFileCache(partFile);

                 fileCache.setFileState(SysPartFileEnum.UPLOADING.getCode());

                 fileCache.setFileInfo( new FileInfo(DEFAULT_BUCKET, objectName, uploadId));

                 fileCache.setFileId(fileId);

                 redisService.setCacheObject(getPartFileKey(partFile.getUid()), fileCache);

             }

             return fileCache;

         } catch (Exception e) {

             log.error( "文件缓存初始化异常:{}" , partFile, e);

             throw new ServiceException(SysFileInfoExceptionEnum.PART_FILE_INIT_CACHE_ERROR);

         } finally {

             lock.unlock();

         }

     }

     /**

      * 获取文件缓存key

      *

      * @param fileId 文件Id

      * @return %s:%s:%s

      */

     private String getPartFileKey(String fileId) {

         return String.format( "%s:%s:%s" , PART_FILE_KEY, LoginContextHolder.me().getSysLoginUserId(), fileId);

     }

}

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

package com.aimilin.common.base.file;

import com.aimilin.common.base.file.param.*;

/**

  * 大文件分片操作服务类

  *

  * @version V1.0

  * @date 2022/5/24 16:56

  */

public interface FilePartOperator extends FileOperator {

     /**

      * 初始化分片文件上传

      *

      * @param bucketName 文件桶

      * @param key        文件key

      * @return 本次文件上传唯一标识

      */

     String initiateMultipartUpload(String bucketName, String key);

     /**

      * 上传分片文件

      *

      * @param fileUploadPart 分片文件参数

      * @return 上传结果

      */

     FileUploadPartResult uploadPart(FileUploadPart fileUploadPart);

     /**

      * 完成分片上传

      *

      * @param completeFileUploadPart 请求对象

      * @return 结果信息

      */

     CompleteFileUploadPartResult completeMultipartUpload(CompleteFileUploadPart completeFileUploadPart);

     /**

      * 取消文件分片上传

      *

      * @param bucketName 文件桶

      * @param objectName 对象key

      * @param uploadId   上传ID

      * @return

      */

     void abortMultipartUpload(String bucketName, String objectName, String uploadId);

}

/**

  * 文件分片上传取消

  *

  * @version V1.0

  * @date 2022/5/24 20:32

  */

public class AbortMultipartUploadResult {

}

/**

  * 完成分片上传

  *

  * @version V1.0

  * @date 2022/5/24 20:07

  */

@Getter

@Setter

@ToString

public class CompleteFileUploadPart implements Serializable {

     private String bucketName;

     private String objectName;

     private String uploadId;

     private List<FileUploadPartResult> partETags;

}

/**

  * 分片上传结果

  *

  * @version V1.0

  * @date 2022/5/24 20:08

  */

@Getter

@Setter

@ToString

public class CompleteFileUploadPartResult implements Serializable {

     private String bucketName;

     private String objectName;

     private String location;

     private String eTag;

}

/**

  * 文件分片上传请求参数

  *

  * @version V1.0

  * @date 2022/5/24 17:00

  */

@Getter

@Setter

@ToString

public class FileUploadPart implements Serializable {

     /**

      * 文件桶

      */

     private String bucketName;

     /**

      * 文件key

      */

     private String objectName;

     /**

      * 文件上传ID

      */

     private String uploadId;

     /**

      * 分片大小,设置分片大小。除了最后一个分片没有大小限制,其他的分片最小为100 KB

      */

     private Long partSize;

     /**

      * 设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出此范围,OSS将返回InvalidArgument错误码。

      */

     private Integer partNumber;

     /**

      * 分片Md5签名

      */

     private String partMd5;

     /**

      * 分片文件内容

      */

     @JsonIgnore

     @JSONField (deserialize = false , serialize = false )

     private InputStream partContent;

}

/**

  * 文件分片上传结果

  *

  * @version V1.0

  * @date 2022/5/24 17:01

  */

@Getter

@Setter

@ToString

public class FileUploadPartResult implements Serializable {

     /**

      * 分块编号

      */

     private Integer partNumber;

     /**

      * 当前分片大小

      */

     private Long partSize;

     /**

      * 上传结果tag

      */

     private String partETag;

}

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

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

/**

  * 本地文件上传操作

  *

  */

@Slf4j

public class LocalFileOperator implements FilePartOperator {

     @Override

     public FileLocationEnum getFileLocation() {

         return FileLocationEnum.LOCAL;

     }

     private final LocalFileProperties localFileProperties;

     private String currentSavePath = "" ;

     private Dict localClient;

     public LocalFileOperator(LocalFileProperties localFileProperties) {

         this .localFileProperties = localFileProperties;

         initClient();

     }

     @Override

     public void initClient() {

         if (SystemUtil.getOsInfo().isWindows()) {

             String savePathWindows = localFileProperties.getLocalFileSavePathWin();

             if (!FileUtil.exist(savePathWindows)) {

                 FileUtil.mkdir(savePathWindows);

             }

             currentSavePath = savePathWindows;

         } else {

             String savePathLinux = localFileProperties.getLocalFileSavePathLinux();

             if (!FileUtil.exist(savePathLinux)) {

                 FileUtil.mkdir(savePathLinux);

             }

             currentSavePath = savePathLinux;

         }

         localClient = Dict.create();

         localClient.put( "currentSavePath" , currentSavePath);

         localClient.put( "localFileProperties" , localFileProperties);

     }

     @Override

     public void destroyClient() {

         // empty

     }

     @Override

     public Object getClient() {

         // empty

         return localClient;

     }

     @Override

     public boolean doesBucketExist(String bucketName) {

         String absolutePath = currentSavePath + File.separator + bucketName;

         return FileUtil.exist(absolutePath);

     }

     @Override

     public void setBucketAcl(String bucketName, BucketAuthEnum bucketAuthEnum) {

         // empty

     }

     @Override

     public boolean isExistingFile(String bucketName, String key) {

         return FileUtil.exist( this .getAbsolutePath(bucketName, key));

     }

     @Override

     public void storageFile(String bucketName, String key, byte [] bytes) {

         // 判断bucket存在不存在

         String bucketPath = currentSavePath + File.separator + bucketName;

         if (!FileUtil.exist(bucketPath)) {

             FileUtil.mkdir(bucketPath);

         }

         // 存储文件

         FileUtil.writeBytes(bytes, this .getAbsolutePath(bucketName, key));

     }

     @Override

     public void storageFile(String bucketName, String key, InputStream inputStream, long fileSize) {

         // 判断bucket存在不存在

         String bucketPath = currentSavePath + File.separator + bucketName;

         if (!FileUtil.exist(bucketPath)) {

             FileUtil.mkdir(bucketPath);

         }

         // 存储文件

         FileUtil.writeFromStream(inputStream, this .getAbsolutePath(bucketName, key));

     }

     @Override

     public byte [] getFileBytes(String bucketName, String key) {

         // 判断文件存在不存在

         String absoluteFile = this .getAbsolutePath(bucketName, key);

         if (!FileUtil.exist(absoluteFile)) {

             String message = StrUtil.format( "文件不存在,bucket={},key={}" , bucketName, key);

             throw new FileServiceException(message);

         } else {

             return FileUtil.readBytes(absoluteFile);

         }

     }

     @Override

     public void setFileAcl(String bucketName, String key, BucketAuthEnum bucketAuthEnum) {

         // empty

     }

     @Override

     public void copyFile(String originBucketName, String originFileKey, String newBucketName, String newFileKey) {

         // 判断文件存在不存在

         String originFile = this .getAbsolutePath(originBucketName, originFileKey);

         if (!FileUtil.exist(originFile)) {

             String message = StrUtil.format( "源文件不存在,bucket={},key={}" , originBucketName, originFileKey);

             throw new FileServiceException(message);

         } else {

             // 拷贝文件

             String destFile = this .getAbsolutePath(newBucketName, newFileKey);

             FileUtil.copy(originFile, destFile, true );

         }

     }

     @Override

     public String getFileAuthUrl(String bucketName, String key, Long timeoutMillis) {

         // empty

         return null ;

     }

     @Override

     public void deleteFile(String bucketName, String key) {

         // 判断文件存在不存在

         String file = this .getAbsolutePath(bucketName, key);

         if (!FileUtil.exist(file)) {

             return ;

         }

         // 删除文件

         FileUtil.del(file);

     }

     /**

      * 初始化分片文件上传

      *

      * @param bucketName 文件桶

      * @param key        文件key

      * @return 本次文件上传唯一标识

      */

     @Override

     public String initiateMultipartUpload(String bucketName, String key) {

         return FileNameUtil.getName(key);

     }

     /**

      * 上传分片文件

      *

      * @param fileUploadPart 分片文件参数

      * @return 上传结果

      */

     @Override

     public FileUploadPartResult uploadPart(FileUploadPart fileUploadPart) {

         String partName = fileUploadPart.getObjectName() + "." + fileUploadPart.getPartNumber();

         this .storageFile(fileUploadPart.getBucketName(), partName, fileUploadPart.getPartContent(), fileUploadPart.getPartSize());

         FileUploadPartResult result = new FileUploadPartResult();

         result.setPartNumber(fileUploadPart.getPartNumber());

         result.setPartSize(fileUploadPart.getPartSize());

         result.setPartETag(partName);

         // TODO 正常文件上传完成之后需要验证文件的分片的MD5值是否与前端传入的值一样

         return result;

     }

     /**

      * 完成分片上传

      *

      * @param completeFileUploadPart 请求对象

      * @return 结果信息

      */

     @Override

     public CompleteFileUploadPartResult completeMultipartUpload(CompleteFileUploadPart completeFileUploadPart) {

         try {

             List<FileUploadPartResult> partETags = completeFileUploadPart.getPartETags();

             String path = this .getAbsolutePath(completeFileUploadPart.getBucketName(), completeFileUploadPart.getObjectName());

             partETags.sort((o1, o2) -> {

                 String p1 = FileNameUtil.extName(o1.getPartETag());

                 String p2 = FileNameUtil.extName(o2.getPartETag());

                 return Integer.valueOf(p1).compareTo(Integer.valueOf(p2));

             });

             Files.createFile(Paths.get(path));

             partETags.forEach(c -> {

                 try {

                     Path partPath = Paths.get( this .getAbsolutePath(completeFileUploadPart.getBucketName(), c.getPartETag()));

                     Files.write(Paths.get(path), Files.readAllBytes(partPath), StandardOpenOption.APPEND);

                     Files.delete(partPath);

                 } catch (IOException e) {

                     log.error( "合并文件失败:{}" , e.getMessage(), e);

                     throw new FileServiceException(e.getMessage());

                 }

             });

             // 文件合并完成之后需要校验文件的MD5值是否与前端传入的一致

             return new CompleteFileUploadPartResult();

         } catch (IOException e) {

             log.error( "合并文件失败:{}" , e.getMessage(), e);

             throw new FileServiceException(e.getMessage());

         }

     }

     /**

      * 取消文件分片上传

      *

      * @param bucketName 文件桶

      * @param objectName 对象key

      * @param uploadId   上传ID

      * @return

      */

     @Override

     public void abortMultipartUpload(String bucketName, String objectName, String uploadId) {

         try {

             Path folder = Paths.get( this .getAbsolutePath(bucketName, objectName)).getParent();

             String partName = objectName + "." ;

             Files.list(folder)

                     .filter(path -> StrUtil.contains(path.toString(), partName))

                     .forEach(path -> {

                         try {

                             Files.delete(path);

                         } catch (IOException e) {

                             log.warn( "删除分片文件失败:{}" , path);

                         }

                     });

         } catch (IOException e) {

             log.error( "取消文件分片上传异常:{}" , e.getMessage(), e);

             throw new FileServiceException(e.getMessage());

         }

     }

     /**

      * 获取文件绝对路径

      *

      * @param bucketName 文件桶

      * @param key        对象key

      * @return

      */

     private String getAbsolutePath(String bucketName, String key) {

         return currentSavePath + File.separator + bucketName + File.separator + key;

     }

}

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

/**

  * 阿里云文件操作

  *

  */

@Slf4j

public class AliyunFileOperator implements FilePartOperator {

     @Override

     public FileLocationEnum getFileLocation() {

         return FileLocationEnum.ALIYUN;

     }

     /**

      * 阿里云文件操作客户端

      */

     private OSS ossClient;

     /**

      * 阿里云oss的配置

      */

     private final AliyunOssProperties aliyunOssProperties;

     public AliyunFileOperator(AliyunOssProperties aliyunOssProperties) {

         this .aliyunOssProperties = aliyunOssProperties;

         this .initClient();

     }

     @Override

     public void initClient() {

         String endpoint = aliyunOssProperties.getEndPoint();

         String accessKeyId = aliyunOssProperties.getAccessKeyId();

         String accessKeySecret = aliyunOssProperties.getAccessKeySecret();

         // 创建OSSClient实例。

         ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

     }

     @Override

     public void destroyClient() {

         ossClient.shutdown();

     }

     @Override

     public Object getClient() {

         return ossClient;

     }

     @Override

     public boolean doesBucketExist(String bucketName) {

         try {

             return ossClient.doesBucketExist(bucketName);

         } catch (OSSException e) {

             throw new AliyunFileServiceException(e);

         } catch (ClientException e) {

             throw new AliyunFileServiceException(e);

         }

     }

     @Override

     public void setBucketAcl(String bucketName, BucketAuthEnum bucketAuthEnum) {

         try {

             if (bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)) {

                 ossClient.setBucketAcl(bucketName, CannedAccessControlList.Private);

             } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)) {

                 ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);

             } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)) {

                 ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicReadWrite);

             }

         } catch (OSSException e) {

             throw new AliyunFileServiceException(e);

         } catch (ClientException e) {

             throw new AliyunFileServiceException(e);

         }

     }

     @Override

     public boolean isExistingFile(String bucketName, String key) {

         try {

             return ossClient.doesObjectExist(bucketName, key);

         } catch (OSSException e) {

             throw new AliyunFileServiceException(e);

         } catch (ClientException e) {

             throw new AliyunFileServiceException(e);

         }

     }

     @Override

     public void storageFile(String bucketName, String key, byte [] bytes) {

         try {

             ossClient.putObject(bucketName, key, new ByteArrayInputStream(bytes));

         } catch (OSSException e) {

             throw new AliyunFileServiceException(e);

         } catch (ClientException e) {

             throw new AliyunFileServiceException(e);

         }

     }

     @Override

     public void storageFile(String bucketName, String key, InputStream inputStream, long fileSize) {

         try {

             String contentType = "application/octet-stream" ;

             if (key.contains( "." )) {

                 contentType = MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(key);

             }

             ObjectMetadata metadata = new ObjectMetadata();

             metadata.setContentType(contentType);

             metadata.setContentLength(fileSize);

             PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, inputStream, metadata);

             ossClient.putObject(putObjectRequest);

         } catch (OSSException e) {

             throw new AliyunFileServiceException(e);

         } catch (ClientException e) {

             throw new AliyunFileServiceException(e);

         }

     }

     @Override

     public byte [] getFileBytes(String bucketName, String key) {

         InputStream objectContent = null ;

         try {

             OSSObject ossObject = ossClient.getObject(bucketName, key);

             objectContent = ossObject.getObjectContent();

             return IoUtil.readBytes(objectContent);

         } catch (OSSException e) {

             throw new AliyunFileServiceException(e);

         } catch (ClientException e) {

             throw new AliyunFileServiceException(e);

         } finally {

             IoUtil.close(objectContent);

         }

     }

     @Override

     public void setFileAcl(String bucketName, String key, BucketAuthEnum bucketAuthEnum) {

         try {

             if (bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)) {

                 ossClient.setObjectAcl(bucketName, key, CannedAccessControlList.Private);

             } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)) {

                 ossClient.setObjectAcl(bucketName, key, CannedAccessControlList.PublicRead);

             } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)) {

                 ossClient.setObjectAcl(bucketName, key, CannedAccessControlList.PublicReadWrite);

             }

         } catch (OSSException e) {

             throw new AliyunFileServiceException(e);

         } catch (ClientException e) {

             throw new AliyunFileServiceException(e);

         }

     }

     @Override

     public void copyFile(String originBucketName, String originFileKey, String newBucketName, String newFileKey) {

         try {

             ossClient.copyObject(originBucketName, originFileKey, newBucketName, newFileKey);

         } catch (OSSException e) {

             throw new AliyunFileServiceException(e);

         } catch (ClientException e) {

             throw new AliyunFileServiceException(e);

         }

     }

     @Override

     public String getFileAuthUrl(String bucketName, String key, Long timeoutMillis) {

         try {

             Date expiration = new Date( new Date().getTime() + timeoutMillis);

             URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);

             return url.toString();

         } catch (OSSException e) {

             throw new AliyunFileServiceException(e);

         } catch (ClientException e) {

             throw new AliyunFileServiceException(e);

         }

     }

     @Override

     public void deleteFile(String bucketName, String key) {

         ossClient.deleteObject(bucketName, key);

     }

     /**

      * 初始化分片文件上传

      *

      * @param bucketName 文件桶

      * @param key        文件key

      * @return 本次文件上传唯一标识

      */

     @Override

     public String initiateMultipartUpload(String bucketName, String key) {

         InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, key);

         InitiateMultipartUploadResult result = ossClient.initiateMultipartUpload(request);

         log.info( "阿里云 初始化分片文件上传:{}" , key);

         return result.getUploadId();

     }

     /**

      * 上传分片文件

      *

      * @param fileUploadPart 分片文件参数

      * @return 上传结果

      */

     @Override

     public FileUploadPartResult uploadPart(FileUploadPart fileUploadPart) {

         UploadPartRequest request = AliyunConvert.INSTANCE.convert(fileUploadPart);

         UploadPartResult result = ossClient.uploadPart(request);

         FileUploadPartResult convert = AliyunConvert.INSTANCE.convert(result);

         convert.setPartSize(fileUploadPart.getPartSize());

         log.info( "阿里云 分片文件上传:{},结果:{}" , fileUploadPart, request);

         return convert;

     }

     /**

      * 完成分片上传

      *

      * @param completeFileUploadPart 请求对象

      * @return 结果信息

      */

     @Override

     public CompleteFileUploadPartResult completeMultipartUpload(CompleteFileUploadPart completeFileUploadPart) {

         List<PartETag> tags = new ArrayList<>();

         for (FileUploadPartResult partETag : completeFileUploadPart.getPartETags()) {

             tags.add( new PartETag(partETag.getPartNumber(), partETag.getPartETag()));

         }

         CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest(

                 completeFileUploadPart.getBucketName(),

                 completeFileUploadPart.getObjectName(),

                 completeFileUploadPart.getUploadId(),

                 tags

         );

         CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(request);

         log.info( "京东云合并文件:{},结果:{}" , completeFileUploadPart, result);

         return AliyunConvert.INSTANCE.convert(result);

     }

     /**

      * 取消文件分片上传

      *

      * @param bucketName 文件桶

      * @param objectName 对象key

      * @param uploadId   上传ID

      * @return

      */

     @Override

     public void abortMultipartUpload(String bucketName, String objectName, String uploadId) {

         AbortMultipartUploadRequest request = new AbortMultipartUploadRequest(bucketName, objectName, uploadId);

         ossClient.abortMultipartUpload(request);

     }

}

京东云对象存储实现

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

package com.aimilin.common.base.file.modular.jdcloud;

import cn.hutool.core.io.IoUtil;

import com.amazonaws.SdkClientException;

import com.amazonaws.services.s3.model.*;

import com.aimilin.common.base.file.FileOperator;

import com.aimilin.common.base.file.FilePartOperator;

import com.aimilin.common.base.file.common.enums.BucketAuthEnum;

import com.aimilin.common.base.file.common.enums.FileLocationEnum;

import com.aimilin.common.base.file.modular.jdcloud.exp.JdCloudFileServiceException;

import com.aimilin.common.base.file.modular.jdcloud.prop.JdCloudConvert;

import com.aimilin.common.base.file.modular.jdcloud.prop.JdCloudOssProperties;

import com.amazonaws.auth.AWSCredentialsProvider;

import com.amazonaws.auth.AWSStaticCredentialsProvider;

import com.amazonaws.client.builder.AwsClientBuilder;

import com.amazonaws.services.s3.AmazonS3;

import com.amazonaws.services.s3.AmazonS3Client;

import com.amazonaws.auth.AWSCredentials;

import com.amazonaws.auth.BasicAWSCredentials;

import com.amazonaws.ClientConfiguration;

import com.aimilin.common.base.file.param.*;

import lombok.extern.slf4j.Slf4j;

import javax.activation.MimetypesFileTypeMap;

import java.io.ByteArrayInputStream;

import java.io.InputStream;

import java.net.URL;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

/**

  * 京东云对象存储

  *

  * @version V1.0

  */

@Slf4j

public class JdCloudFileOperator implements FilePartOperator {

     @Override

     public FileLocationEnum getFileLocation() {

         return FileLocationEnum.JDCLOUD;

     }

     /**

      * 京东云客户端

      */

     private AmazonS3 ossClient;

     /**

      * 京东云oss的配置

      */

     private final JdCloudOssProperties jdCloudOssProperties;

     /**

      *

      * @param jdCloudOssProperties

      */

     public JdCloudFileOperator(JdCloudOssProperties jdCloudOssProperties) {

         this .jdCloudOssProperties = jdCloudOssProperties;

         this .initClient();

     }

     @Override

     public void initClient() {

         ClientConfiguration config = new ClientConfiguration();

         AwsClientBuilder.EndpointConfiguration endpointConfig =

                 new AwsClientBuilder.EndpointConfiguration(jdCloudOssProperties.getEndPoint(), jdCloudOssProperties.getSigningRegion());

         AWSCredentials awsCredentials = new BasicAWSCredentials(jdCloudOssProperties.getAccessKeyID(),jdCloudOssProperties.getAccessKeySecret());

         AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);

         ossClient = AmazonS3Client.builder()

                 .withEndpointConfiguration(endpointConfig)

                 .withClientConfiguration(config)

                 .withCredentials(awsCredentialsProvider)

                 .disableChunkedEncoding()

                 .build();

     }

     @Override

     public void destroyClient() {

         ossClient.shutdown();

     }

     @Override

     public Object getClient() {

         return ossClient;

     }

     @Override

     public boolean doesBucketExist(String bucketName) {

         return ossClient.doesBucketExistV2(bucketName);

     }

     @Override

     public void setBucketAcl(String bucketName, BucketAuthEnum bucketAuthEnum) {

         try {

             if (bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)) {

                 ossClient.setBucketAcl(bucketName, CannedAccessControlList.Private);

             } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)) {

                 ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);

             } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)) {

                 ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicReadWrite);

             }

         } catch (Exception e) {

             log.error( "JdCloud-oss-设置预定义策略异常" ,e);

             throw new JdCloudFileServiceException(e);

         }

     }

     @Override

     public boolean isExistingFile(String bucketName, String key) {

         try {

             return ossClient.doesObjectExist(bucketName, key);

         } catch (Exception e) {

             log.error( "JdCloud-oss-判断是否存在文件异常" ,e);

             throw new JdCloudFileServiceException(e);

         }

     }

     @Override

     public void storageFile(String bucketName, String key, byte [] bytes) {

         try {

             InputStream is = new ByteArrayInputStream(bytes);

             ObjectMetadata metadata = new ObjectMetadata();

             metadata.setContentType( "text/plain" );

             metadata.setContentLength(( long )bytes.length);

             ossClient.putObject(bucketName, key, is, metadata);

         } catch (Exception e) {

             log.error( "JdCloud-oss-存储文件异常" ,e);

             throw new JdCloudFileServiceException(e);

         }

     }

     @Override

     public void storageFile(String bucketName, String key, InputStream inputStream, long fileSize) {

         try {

             String contentType = "application/octet-stream" ;

             if (key.contains( "." )) {

                 contentType = MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(key);

             }

             ObjectMetadata metadata = new ObjectMetadata();

             metadata.setContentType(contentType);

             metadata.setContentLength(fileSize);

             ossClient.putObject(bucketName, key, inputStream, metadata);

         } catch (Exception e) {

             log.error( "JdCloud-oss-存储文件异常" ,e);

             throw new JdCloudFileServiceException(e);

         }

     }

     @Override

     public byte [] getFileBytes(String bucketName, String key) {

         InputStream objectContent = null ;

         try {

             S3Object s3Object = ossClient.getObject(bucketName, key);

             objectContent = s3Object.getObjectContent();

             return IoUtil.readBytes(objectContent);

         } catch (Exception e) {

             log.error( "JdCloud-oss-获取某个bucket下的文件字节异常" ,e);

             throw new JdCloudFileServiceException(e);

         } finally {

             IoUtil.close(objectContent);

         }

     }

     @Override

     public void setFileAcl(String bucketName, String key, BucketAuthEnum bucketAuthEnum) {

         try {

             if (bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)) {

                 ossClient.setObjectAcl(bucketName, key, CannedAccessControlList.Private);

             } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)) {

                 ossClient.setObjectAcl(bucketName, key, CannedAccessControlList.PublicRead);

             } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)) {

                 ossClient.setObjectAcl(bucketName, key, CannedAccessControlList.PublicReadWrite);

             }

         } catch (Exception e) {

             log.error( "JdCloud-oss-文件访问权限管理异常" ,e);

             throw new JdCloudFileServiceException(e);

         }

     }

     @Override

     public void copyFile(String originBucketName, String originFileKey, String newBucketName, String newFileKey) {

         try {

             ossClient.copyObject(originBucketName, originFileKey, newBucketName, newFileKey);

         } catch (Exception e) {

             log.error( "JdCloud-oss-拷贝文件异常" ,e);

             throw new JdCloudFileServiceException(e);

         }

     }

     @Override

     public String getFileAuthUrl(String bucketName, String key, Long timeoutMillis) {

         try {

             Date expiration = new Date(System.currentTimeMillis() + timeoutMillis);

             URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);

             return url.toString();

         } catch (Exception e) {

             log.error( "JdCloud-oss-获取文件的下载地址异常" ,e);

             throw new JdCloudFileServiceException(e);

         }

     }

     @Override

     public void deleteFile(String bucketName, String key) {

         try {

             ossClient.deleteObject(bucketName, key);

         } catch (Exception e) {

             log.error( "JdCloud-oss-删除文件异常" , e);

             throw new JdCloudFileServiceException(e);

         }

     }

     /**

      * 初始化分片文件上传

      *

      * @param bucketName 文件桶

      * @param key        文件key

      * @return 本次文件上传唯一标识

      */

     @Override

     public String initiateMultipartUpload(String bucketName, String key) {

         InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, key);

         InitiateMultipartUploadResult initiateMultipartUploadResult = ossClient.initiateMultipartUpload(request);

         log.info( "京东云 初始化分片文件上传:{}" , key);

         return initiateMultipartUploadResult.getUploadId();

     }

     /**

      * 上传分片文件

      *

      * @param fileUploadPart 分片文件参数

      * @return 上传结果

      */

     @Override

     public FileUploadPartResult uploadPart(FileUploadPart fileUploadPart) {

         UploadPartRequest request = JdCloudConvert.INSTANCE.convert(fileUploadPart);

         UploadPartResult uploadPartResult = ossClient.uploadPart(request);

         FileUploadPartResult result = JdCloudConvert.INSTANCE.convert(uploadPartResult.getPartETag());

         result.setPartSize(fileUploadPart.getPartSize());

         log.info( "京东云 分片文件上传:{},结果:{}" , fileUploadPart, request);

         return result;

     }

     /**

      * 完成分片上传

      *

      * @param completeFileUploadPart 请求对象

      * @return 结果信息

      */

     @Override

     public CompleteFileUploadPartResult completeMultipartUpload(CompleteFileUploadPart completeFileUploadPart) {

         CompleteMultipartUploadRequest request = JdCloudConvert.INSTANCE.convert(completeFileUploadPart);

         CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(request);

         log.info( "京东云合并文件:{},结果:{}" , completeFileUploadPart, result);

         return JdCloudConvert.INSTANCE.convert(result);

     }

     /**

      * 取消文件分片上传

      *

      * @param bucketName 文件桶

      * @param objectName 对象key

      * @param uploadId   上传ID

      * @return

      */

     @Override

     public void abortMultipartUpload(String bucketName, String objectName, String uploadId) {

         ossClient.abortMultipartUpload( new AbortMultipartUploadRequest(bucketName, objectName, uploadId));

     }

}

腾讯云对象存储分片上传

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

package com.aimilin.common.base.file.modular.tencent;

import cn.hutool.core.io.IoUtil;

import com.aimilin.common.base.file.FilePartOperator;

import com.aimilin.common.base.file.common.enums.FileLocationEnum;

import com.aimilin.common.base.file.modular.aliyun.prop.AliyunConvert;

import com.aimilin.common.base.file.modular.tencent.prop.TenConvert;

import com.aimilin.common.base.file.param.CompleteFileUploadPart;

import com.aimilin.common.base.file.param.CompleteFileUploadPartResult;

import com.aimilin.common.base.file.param.FileUploadPart;

import com.aimilin.common.base.file.param.FileUploadPartResult;

import com.qcloud.cos.COSClient;

import com.qcloud.cos.ClientConfig;

import com.qcloud.cos.auth.BasicCOSCredentials;

import com.qcloud.cos.auth.COSCredentials;

import com.qcloud.cos.exception.CosClientException;

import com.qcloud.cos.exception.CosServiceException;

import com.qcloud.cos.http.HttpMethodName;

import com.qcloud.cos.model.*;

import com.qcloud.cos.region.Region;

import com.qcloud.cos.transfer.TransferManager;

import com.qcloud.cos.transfer.TransferManagerConfiguration;

import com.aimilin.common.base.file.FileOperator;

import com.aimilin.common.base.file.common.enums.BucketAuthEnum;

import com.aimilin.common.base.file.modular.tencent.exp.TencentFileServiceException;

import com.aimilin.common.base.file.modular.tencent.prop.TenCosProperties;

import lombok.extern.slf4j.Slf4j;

import javax.activation.MimetypesFileTypeMap;

import java.io.ByteArrayInputStream;

import java.io.InputStream;

import java.net.URL;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

/**

  * 腾讯云内网文件操作

  *

  */

@Slf4j

public class TenFileOperator implements FilePartOperator {

     @Override

     public FileLocationEnum getFileLocation() {

         return FileLocationEnum.TENCENT;

     }

     private final TenCosProperties tenCosProperties;

     private COSClient cosClient;

     private TransferManager transferManager;

     public TenFileOperator(TenCosProperties tenCosProperties) {

         this .tenCosProperties = tenCosProperties;

         initClient();

     }

     @Override

     public void initClient() {

         // 1.初始化用户身份信息

         String secretId = tenCosProperties.getSecretId();

         String secretKey = tenCosProperties.getSecretKey();

         COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);

         // 2.设置 bucket 的区域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224

         Region region = new Region(tenCosProperties.getRegionId());

         ClientConfig clientConfig = new ClientConfig(region);

         // 3.生成 cos 客户端。

         cosClient = new COSClient(cred, clientConfig);

         // 4.线程池大小,建议在客户端与 COS 网络充足(例如使用腾讯云的 CVM,同地域上传 COS)的情况下,设置成16或32即可,可较充分的利用网络资源

         // 对于使用公网传输且网络带宽质量不高的情况,建议减小该值,避免因网速过慢,造成请求超时。

         ExecutorService threadPool = Executors.newFixedThreadPool( 32 );

         // 5.传入一个 threadpool, 若不传入线程池,默认 TransferManager 中会生成一个单线程的线程池。

         transferManager = new TransferManager(cosClient, threadPool);

         // 6.设置高级接口的分块上传阈值和分块大小为10MB

         TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();

         transferManagerConfiguration.setMultipartUploadThreshold( 10 * 1024 * 1024 );

         transferManagerConfiguration.setMinimumUploadPartSize( 10 * 1024 * 1024 );

         transferManager.setConfiguration(transferManagerConfiguration);

     }

     @Override

     public void destroyClient() {

         cosClient.shutdown();

     }

     @Override

     public Object getClient() {

         return cosClient;

     }

     @Override

     public boolean doesBucketExist(String bucketName) {

         try {

             return cosClient.doesBucketExist(bucketName);

         } catch (CosServiceException e) {

             throw new TencentFileServiceException(e);

         } catch (CosClientException e) {

             throw new TencentFileServiceException(e);

         }

     }

     @Override

     public void setBucketAcl(String bucketName, BucketAuthEnum bucketAuthEnum) {

         try {

             if (bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)) {

                 cosClient.setBucketAcl(bucketName, CannedAccessControlList.Private);

             } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)) {

                 cosClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);

             } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)) {

                 cosClient.setBucketAcl(bucketName, CannedAccessControlList.PublicReadWrite);

             }

         } catch (CosServiceException e) {

             throw new TencentFileServiceException(e);

         } catch (CosClientException e) {

             throw new TencentFileServiceException(e);

         }

     }

     @Override

     public boolean isExistingFile(String bucketName, String key) {

         try {

             cosClient.getObjectMetadata(bucketName, key);

             return true ;

         } catch (CosServiceException e) {

             return false ;

         }

     }

     @Override

     public void storageFile(String bucketName, String key, byte [] bytes) {

         // 根据文件名获取contentType

         String contentType = "application/octet-stream" ;

         if (key.contains( "." )) {

             contentType = MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(key);

         }

         // 上传文件

         ByteArrayInputStream byteArrayInputStream = null ;

         try {

             byteArrayInputStream = new ByteArrayInputStream(bytes);

             ObjectMetadata objectMetadata = new ObjectMetadata();

             objectMetadata.setContentType(contentType);

             cosClient.putObject(bucketName, key, new ByteArrayInputStream(bytes), objectMetadata);

         } catch (CosServiceException e) {

             throw new TencentFileServiceException(e);

         } catch (CosClientException e) {

             throw new TencentFileServiceException(e);

         } finally {

             IoUtil.close(byteArrayInputStream);

         }

     }

     @Override

     public void storageFile(String bucketName, String key, InputStream inputStream, long fileSize) {

         // 根据文件名获取contentType

         String contentType = "application/octet-stream" ;

         if (key.contains( "." )) {

             contentType = MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(key);

         }

         // 上传文件

         try {

             ObjectMetadata objectMetadata = new ObjectMetadata();

             objectMetadata.setContentType(contentType);

             objectMetadata.setContentLength(fileSize);

             cosClient.putObject(bucketName, key, inputStream, objectMetadata);

         } catch (CosServiceException e) {

             throw new TencentFileServiceException(e);

         } catch (CosClientException e) {

             throw new TencentFileServiceException(e);

         } finally {

             IoUtil.close(inputStream);

         }

     }

     @Override

     public byte [] getFileBytes(String bucketName, String key) {

         COSObjectInputStream cosObjectInput = null ;

         try {

             GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key);

             COSObject cosObject = cosClient.getObject(getObjectRequest);

             cosObjectInput = cosObject.getObjectContent();

             return IoUtil.readBytes(cosObjectInput);

         } catch (CosServiceException e) {

             throw new TencentFileServiceException(e);

         } catch (CosClientException e) {

             throw new TencentFileServiceException(e);

         } finally {

             IoUtil.close(cosObjectInput);

         }

     }

     @Override

     public void setFileAcl(String bucketName, String key, BucketAuthEnum bucketAuthEnum) {

         if (bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)) {

             cosClient.setObjectAcl(bucketName, key, CannedAccessControlList.Private);

         } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)) {

             cosClient.setObjectAcl(bucketName, key, CannedAccessControlList.PublicRead);

         } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)) {

             cosClient.setObjectAcl(bucketName, key, CannedAccessControlList.PublicReadWrite);

         }

     }

     @Override

     public void copyFile(String originBucketName, String originFileKey, String newBucketName, String newFileKey) {

         // 初始化拷贝参数

         Region srcBucketRegion = new Region(tenCosProperties.getRegionId());

         CopyObjectRequest copyObjectRequest = new CopyObjectRequest(

                 srcBucketRegion, originBucketName, originFileKey, newBucketName, newFileKey);

         // 拷贝对象

         try {

             transferManager.copy(copyObjectRequest, cosClient, null );

         } catch (CosServiceException e) {

             throw new TencentFileServiceException(e);

         } catch (CosClientException e) {

             throw new TencentFileServiceException(e);

         }

     }

     @Override

     public String getFileAuthUrl(String bucketName, String key, Long timeoutMillis) {

         GeneratePresignedUrlRequest presignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, key, HttpMethodName.GET);

         Date expirationDate = new Date(System.currentTimeMillis() + timeoutMillis);

         presignedUrlRequest.setExpiration(expirationDate);

         URL url = null ;

         try {

             url = cosClient.generatePresignedUrl(presignedUrlRequest);

         } catch (CosServiceException e) {

             throw new TencentFileServiceException(e);

         } catch (CosClientException e) {

             throw new TencentFileServiceException(e);

         }

         return url.toString();

     }

     @Override

     public void deleteFile(String bucketName, String key) {

         cosClient.deleteObject(bucketName, key);

     }

     /**

      * 初始化分片文件上传

      *

      * @param bucketName 文件桶

      * @param key        文件key

      * @return 本次文件上传唯一标识

      */

     @Override

     public String initiateMultipartUpload(String bucketName, String key) {

         InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, key);

         InitiateMultipartUploadResult result = cosClient.initiateMultipartUpload(request);

         log.info( "腾讯云 初始化分片文件上传:{}" , key);

         return result.getUploadId();

     }

     /**

      * 上传分片文件

      *

      * @param fileUploadPart 分片文件参数

      * @return 上传结果

      */

     @Override

     public FileUploadPartResult uploadPart(FileUploadPart fileUploadPart) {

         UploadPartRequest request = TenConvert.INSTANCE.convert(fileUploadPart);

         UploadPartResult result = cosClient.uploadPart(request);

         FileUploadPartResult convert = TenConvert.INSTANCE.convert(result);

         convert.setPartSize(fileUploadPart.getPartSize());

         log.info( "腾讯云 分片文件上传:{},结果:{}" , fileUploadPart, request);

         return convert;

     }

     /**

      * 完成分片上传

      *

      * @param completeFileUploadPart 请求对象

      * @return 结果信息

      */

     @Override

     public CompleteFileUploadPartResult completeMultipartUpload(CompleteFileUploadPart completeFileUploadPart) {

         List<PartETag> tags = new ArrayList<>();

         for (FileUploadPartResult partETag : completeFileUploadPart.getPartETags()) {

             tags.add( new PartETag(partETag.getPartNumber(), partETag.getPartETag()));

         }

         CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest(

                 completeFileUploadPart.getBucketName(),

                 completeFileUploadPart.getObjectName(),

                 completeFileUploadPart.getUploadId(),

                 tags);

         CompleteMultipartUploadResult result = cosClient.completeMultipartUpload(request);

         log.info( "京东云合并文件:{},结果:{}" , completeFileUploadPart, result);

         return TenConvert.INSTANCE.convert(result);

     }

     /**

      * 取消文件分片上传

      *

      * @param bucketName 文件桶

      * @param objectName 对象key

      * @param uploadId   上传ID

      * @return

      */

     @Override

     public void abortMultipartUpload(String bucketName, String objectName, String uploadId) {

         AbortMultipartUploadRequest request = new AbortMultipartUploadRequest(bucketName, objectName, uploadId);

         cosClient.abortMultipartUpload(request);

     }

}

分片上传前端代码实现

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

<template>

   <div class= "upload__wrap" :class= "`upload__wrap--${size}`" >

     <div class= "files" v- for = "img in existsImgs" :key= "img.id" >

       <template v- if = "pictureType.includes(handleType(img.fileSuffix))" >

         <!-- 图片类型 -->

         <img style= "object-fit: cover;" width= "104" height= "104" :src= "handleImg(img.id, 208, 208)" />

         <div class= "btn__wraps" >

           <div class= "btn__innerwraps" >

             <a-icon class= "icon__btn" type= "eye" @click= "$refs.previewForm.preview({ id: img.id })" />

             <a-popconfirm placement= "topRight" title= "确认删除?" @confirm= "() => deleteImg(img.id)" >

               <a-icon class= "icon__btn" type= "delete" />

             </a-popconfirm>

           </div>

         </div>

       </template>

       <template v- else - if = "threedType.includes(handleType(img.fileSuffix))" >

         <img

           style= "object-fit: cover;cursor: pointer;"

           width= "104"

           height= "104"

           src= "https://aimilin.com/stata/test.png"

         />

         <div class= "btn__wraps" >

           <div class= "btn__innerwraps" >

             <a-icon class= "icon__btn" type= "eye" @click= "show3dModal(img)" />

             <a-popconfirm placement= "topRight" title= "确认删除?" @confirm= "() => deleteImg(img.id)" >

               <a-icon class= "icon__btn" type= "delete" />

             </a-popconfirm>

           </div>

         </div>

       </template>

       <template v- else >

         当前类型文件暂不支持预览

       </template>

     </div>

     <div class= "tempimg__placeholder" v- for = "temp in tempImgArr" :key= "temp.uid" >上传中…</div>

     <a-upload

       name= "upload"

       :list-type= "listType"

       :file-list= "fileList"

       :accept= "format"

       :multiple= "multiple"

       :before-upload= "beforeUpload"

       :customRequest= "customRequest"

     >

       <div v- if = "existsImgs.length + tempImgArr.length < maxPicsLength" >

         <a-icon type= "plus" />

         <div class= "ant-upload-text" >

           上传

         </div>

       </div>

     </a-upload>

     <preview-form ref= "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'

import previewForm from '@/views/system/file/previewForm.vue'

import Preview3dModel from '@/views/system/file/preview3dmodel.vue'

import { handleImg } from '@/utils/util'

import SparkMD5 from 'spark-md5'

import { SUCCESS, SERVICE_ERROR, UPLOADING } from '@/assets/js/responseCode'

 

const SIZEUNIT = 1 * 1024 * 1024

export default {

   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 = null

     console.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) {

       return filetType.indexOf( '.' ) > -1 ? filetType : '.' + filetType

     },

     beforeUpload(file, fileList) {

       console.log( 'this' , this )

       return new Promise((resolve, reject) => {

         let type = file.type

         if (!type) {

           type = '.' + file.name.split( '.' ).pop()

         }

         const isFormatFiles = this .format.replace(/\s*/g, '' ).split( ',' ).includes(type)

         if (!isFormatFiles) {

           this .$message.error(`只支持以下${ this .format}格式!`)

           return reject( new Error(`只支持以下${ this .format}格式!`))

         }

         const maxSizeLimit = this .threedType.includes(type) ? 100 : 20

         const isLtMaxSize = file.size / SIZEUNIT < maxSizeLimit

         if (!isLtMaxSize) {

           this .$message.error(`图片须小于${maxSizeLimit}MB!`)

           return reject( new Error(`图片须小于${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)

           return reject( new Error(`最多只能上传${ this .maxPicsLength}张!`))

         }

         this .isStopUpload = false

         // this.loading = true

         this .$set( this , 'tempImgArr' , [... this .tempImgArr, file.uid])

         this .$emit( 'imgUploadingStatus' , [... this .tempImgArr, file.uid].length)

         this .$emit( 'resetUploadStatus' )

         resolve( true )

       })

       // return isFormatFiles && isLt2M

     },

     preview (id) {

       this .$refs.previewForm.preview({ id })

     },

     deleteImg (id) {

       this .$emit( 'deletePic' , id)

     },

     /**

      * 上传文件

      */

     customRequest (data) {

         const fileType = '.' + data.file.name.split( '.' ).pop()

         const fileReader = new FileReader()

         const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice

         let currentChunk = 0

         const chunkSize = 4 * 1024 * 1024

         const chunks = Math.ceil(data.file.size / chunkSize)

         const spark = new SparkMD5.ArrayBuffer()

         const partChunksArr = []

         const fileData = {}

         loadNext()

         fileReader.onload = e => {

           spark.append(e.target.result)

           const sparkChunk = new SparkMD5.ArrayBuffer()

           sparkChunk.append(e.target.result)

           const partMd5 = sparkChunk.end()

           partChunksArr.push({

             file: fileData[currentChunk],

             partNumber: currentChunk + 1,

             partMd5,

             partSize: chunkSize,

             totalSize: data.file.size

           })

           currentChunk++

           if (currentChunk < chunks) {

             loadNext()

           } else {

             const md5 = spark.end()

             this .finalUploadFn(partChunksArr, fileType, data, md5)

           }

         }

 

         fileReader.onerror = function () {

           this .$message.error(`文件${data.file.name}读取出错,请检查该文件`)

           // data.cancel()

         }

 

         function loadNext() {

           const start = currentChunk * chunkSize

           const end = ((start + chunkSize) >= data.file.size) ? data.file.size : start + chunkSize

           const currentChunkData = blobSlice.call(data.file, start, end)

           fileReader.readAsArrayBuffer(currentChunkData)

           fileData[currentChunk] = currentChunkData

         }

     },

     finalUploadFn (formData, fileType, data, wholeFileMd5) {

       formData.forEach(item => {

         const newFormData = new FormData()

         // 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 = false

         if (res.code === SUCCESS && res.data?.fileState === SUCCESS) {

           this .$emit( 'getNewPics' , {

             id: res.data.fileId,

             fileSuffix: fileType

           })

           const newTempImgArr = this .tempImgArr.filter(item => item !== res.data?.uid)

           this .$set( this , 'tempImgArr' , newTempImgArr)

           this .$emit( 'imgUploadingStatus' , newTempImgArr.length)

           // this.$refs.table.refresh()

         } else if (res.code === SUCCESS && res.data?.fileState === UPLOADING) {

         } else if (res.code === SUCCESS && res.data?.fileState === SERVICE_ERROR) {

           if (! this .failupload) {

             this .failupload = {}

             this .failupload[data.file.uid] = data.file.uid

             sysFileInfoPartUpload(newFormData)

           } else {

             if (! this .failupload[data.file.uid]) {

                 sysFileInfoPartUpload(newFormData)

                 this .failupload[data.file.uid] = data.file.uid

             }

           }

         } else if (res.code !== SUCCESS) {

           // 上传失败,从占位图中移除一个

           const newTempImgArr = this .tempImgArr

           newTempImgArr.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 => {

         const newTempImgArr = this .tempImgArr

         newTempImgArr.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>

/* you can make up upload button and sample style by using stylesheets */

.ant-upload-select-picture-card i {

   font-size: 32px;

   color: #999;

}

 

.ant-upload-select-picture-card .ant-upload-text {

   margin-top: 8px;

   color: #666;

}

</style>

<style lang= "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: all 0.3s linear;

       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: all 0.3s linear;

         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: 1px solid #d9d9d9;

     margin-right: 10px;

     margin-bottom: 10px;

   }

}

</style>

参考资料:

项目参考地址: https://gitee.com/donghuangtaiyi/file-uploader

到此这篇关于Java超详细大文件分片上传代码的文章就介绍到这了,更多相关Java大文件上传内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

原文链接:https://blog.csdn.net/afgasdg/article/details/125136122

查看更多关于Java超详细大文件分片上传代码的详细内容...

  阅读:21次