大文件上传主要功能:上传进度,md5文件计算,重复上传实现秒传功能。
主要实现思路
1.前端使用FileReader对文件进行分片。
2.后台调用Minio接口进行分片上传。
3.上传完成后,调用compose方法进行组合。
4.业务表存的是文件的ID,多个文件用“,”隔开。
组件的使用
<el-form-item>
<upload-file ref="uploadFile" :limit-size="1024" :limit="8" :ids.sync="form.attribute1" />
</el-form-item>
注意 在表单提交或关闭弹出框是一定要清除件内的数据;如在弹出对话框内新增数据关闭时调用组件内的clear方法:
<el-dialog
:visible.sync="dialogFormVisible"
fullscreen
:modal="false"
@close="handleCloseDialog"
show-close
>
...表单...
</el-dialog>
handleCloseDialog(){
this.$refs.uploadFile.clear();
}
实现效果
前端实现
依赖包 spark-md5 前端进行文件md5计算使用。 安装 npm install spark-md5 --save
BigFileUpload.vue 组件
主要功能:上传、展示文件列表,文件删除更新操作。
<template>
<div
v-loading="loading"
style="width: 100%;overflow: hidden;padding-bottom:20px"
class="file-wrapper"
:element-loading-text="loadingText"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.3)"
>
<h3> <span v-show="required" style="color:#F56C6C;font-weight: bolder;font-size: 16px;margin-right: 5px ">*</span>{{label}}</h3>
<div v-show="clearable" style="width: 300px;height: 40px;position: relative">
<input ref="fileUpload" type="file" multiple style="width: 300px;height: 40px;position: absolute;left: 0;opacity: 0;z-index: 10;cursor: pointer" @change="handleFileChange" >
<el-button type="primary" size="mini" style="margin-left: 20px;width: 200px" >选取文件</el-button>
</div>
<div>
<el-table
border
ref="table"
:data="fileList"
>
<el-table-column
type="index"
min-width="50"
/>
<el-table-column
prop="fileName"
label="文件名"
min-width="150"
/>
<el-table-column
prop="fileSize"
label="大小(M)"
min-width="80"
:formatter="(row, column, cellValue, index) => cellValue ? (cellValue/1024/1024).toFixed(3) : 0 "
/>
<el-table-column
prop="fileSize"
label="md5"
min-width="80"
>
<template slot-scope="{row}">
{{ row.md5 ? 'md5计算完成': 'md5未计算' }}
</template>
</el-table-column>
<el-table-column
prop="percent"
label="上传进度(%)"
min-width="100"
>
<template slot-scope="{row}">
{{ row.percent }}
</template>
</el-table-column>
<el-table-column
align="center"
:label="$t( $constant.TRANSLATION_SYSTEM+ '.'+'table_column_operate' )"
min-width="150"
>
<template slot-scope="scope">
<el-link icon="el-icon-download" v-show="scope.row.status" @click="handleDownload(scope.row)">下载</el-link>
<el-button v-show="clearable" type="text" size="medium" @click="handleDeleteFile(scope.row)" style="margin-left: 5px;position: relative;top:2px">{{ $t( $constant.TRANSLATION_SYSTEM +'.btn_del') }}</el-button>
<el-button v-show="!scope.row.status" type="text" size="medium" @click="handleUploadFile(scope.row)" style="margin-left: 5px;position: relative;top:2px">{{ $t( $constant.TRANSLATION_SYSTEM +'.upload','上传') }}</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
import { getToken } from '@/utils/auth'
import axios from 'axios'
import { mapGetters } from 'vuex';
import { downloadFile, deleteFile } from '@/api/file';
import FileUpload from "@/utils/fileUpload";
export default {
name:"UploadBigFile",
props: {
limit: {
type: Number,
default: 10
},
ids: {
type: String,
default: ''
},
clearable: {
type: Boolean,
default: true
},
disabled: {
type: Boolean,
default: true
},
limitSize:{
type:Number,
default:10 // unit M
},
label:{
type: String,
default:'附件'
},
required:{
default: false
}
},
data() {
return {
fileList: [],
originFileList:[],
action: process.env.VUE_APP_BASE_API,
loading: false,
loadingText:''
}
},
computed: {
...mapGetters(['user']),
headers() {
return {
Authorization: 'Bearer ' + getToken(),
TENANTID: this.user.tenantId
}
}
},
watch: {
ids: {
immediate: true,
handler(val) {
if (val) {
axios.get(this.action + '/tccloud-mdm/file/getFiles?ids=' + val, {
headers: {
...this.headers
}
}).then(res => {
if (res.status === 200) {
res.data.forEach(item=>{
const file = this.fileList.find(file => file.fileName === item.fileName)
if(file){
Object.assign(file,item,{status:true,md5:'ok',percent:100})
console.log("update===>",file);
}
else{
console.log("unshift===>",item);
this.fileList.unshift( Object.assign(item,{status:true,md5:'ok',percent:100}));
}
})
this.$refs.table.doLayout()
}
})
}
}
}
},
mounted() {
this.$refs.table.doLayout()
},
methods: {
handleFileChange(e){
const files = e.target.files
console.log(files);
for(let i = 0; i < files.length; i++){
const file= files[i]
const f = this.originFileList.findIndex(item=> file.name ===item.name )
if(f > -1){
this.msgError(file.name + "文件重名或已经存在")
continue;
}
this.originFileList.push(file);
this.fileList.push( {
id: -new Date().getTime(),
fileName:file.name,
fileSize:file.size,
type:file.type,
md5:'',
status:false,
percent:0,
fileUrl:'',
})
}
e.target.value=null
},
handleDeleteFile(file) {
let index= this.fileList.findIndex(item=> item.id === file.id)
this.fileList.splice(index,1);
index = this.originFileList.findIndex(item=> item.name === file.fileName)
this.originFileList.splice(index,1);
if(file.id < 0){
return;
}
this.loading = true;
deleteFile(file.id).then(res => {
this.loading = false;
this.updateFileId()
this.msgSuccess('删除成功')
}).catch(e => {
this.loading = false;
this.msgError('删除文件失败')
})
},
updateFileId() {
console.log('==========updateFileId============>',this.fileList)
const ids = this.getFileIds(this.fileList)
this.$emit('update:ids', ids)
},
getFileIds(fileList) {
if (!fileList || fileList.length === 0) return ''
let ids = ''
fileList.forEach((item, index) => {
if(item.id < 0) return;
if (!ids) {
ids += item.id
} else {
ids = ids + ',' + item.id
}
})
return ids
},
handleDownload(row) {
console.log(row)
this.loadingText = "";
this.loading = true
downloadFile(row.id).then(res => {
const a = document.createElement('a')
a.style.display = 'none'
a.download = row.fileName
a.href = window.URL.createObjectURL(res)
a.click()
a.remove()
this.loading = false
})
},
async handleUploadFile(row){
this.loading = true
const file = this.originFileList.find(file=> file.name === row.fileName);
const fileShadow = this.fileList.find(file=> file.fileName === row.fileName);
// 文件小于4M直接上传
if(file.size < 4 * 1024 * 1024){
const res = await this.uploadSmallFile(file);
Object.assign(fileShadow,res,{status:true,md5:'ok',percent:100})
console.log('==========>',fileShadow);
this.loading = false;
this.updateFileId();
return;
}
this.loadingText='md5计算 0%'
const upload = new FileUpload(file);
row.md5 = await upload.calculateMD5(info=>{
this.loadingText= 'md5计算:'+ info + '%';
});
upload.startUpload(this.headers,this.action + '/tccloud-mdm/file/uploadBigFile',info=>{
fileShadow.percent = info;
this.loadingText="文件上传:" +info + '%';
},res=>{
console.log("complete")
console.log(res)
Object.assign(fileShadow,res,{status:true,md5:'ok',percent:100})
this.updateFileId();
this.loading = false;
})
},
async uploadSmallFile(file){
const formData = new FormData();
formData.append('file', file);
const res = await axios.post(this.action + '/tccloud-mdm/file/upload',formData,{
headers:this.headers
})
return res.data
},
clear(){
this.fileList = [];
this.originFileList =[]
}
}
}
</script>
<style lang="scss" scoped>
.file-wrapper {
a:hover{
color: #52de38;
}
}
</style>
FileUpload工具类
主要功能,文件分片,文件上传。
import SparkMD5 from 'spark-md5';
import axios from "axios";
export default class FileUpload {
constructor(file) {
this.file= file;
this.chunkSize = 1024*6*1024; // 6MB per chunk
this.totalChunks = Math.ceil(file.size / this.chunkSize);
this.spark = new SparkMD5.ArrayBuffer();
this.fileReader = new FileReader();
this.chunkIndex = 0;
this.md5 = '';
}
calculateMD5(onProcess) {
return new Promise((resolve, reject) => {
this.fileReader.onload = (e) =>{
this.spark.append(e.target.result); // Append array buffer
this.chunkIndex++;
if (this.chunkIndex < this.totalChunks) {
this.loadNext();
onProcess && onProcess((this.chunkIndex / this.totalChunks * 100).toFixed(2))
} else {
this.md5 = this.spark.end(); // Compute MD5 hash
resolve(this.md5)
}
};
this.fileReader.onerror = function () {
reject("File read error")
};
this.loadNext();
})
}
loadNext() {
const start = this.chunkIndex * this.chunkSize;
const end = Math.min(this.file.size, start + this.chunkSize);
this.fileReader.readAsArrayBuffer(this.file.slice(start, end));
}
startUpload(headers,path,onProcess,onComplete){
this.fileReader = new FileReader();
this.chunkIndex = 0;
this.fileReader.onload = (e) =>{
const formData = new FormData();
formData.append('chunk', new Blob([e.target.result], { type: this.file.type }));
formData.append('chunkIndex', this.chunkIndex);
formData.append('totalChunks', this.totalChunks);
formData.append('fileName', this.file.name);
formData.append('totalSize', this.file.size);
formData.append("md5",this.md5)
onProcess((this.chunkIndex / this.totalChunks * 100).toFixed(2) );
axios.post(path,formData,{
headers:headers
}).then(res=>{
// 秒传
if(res&& res.data){
console.log("-------------秒传------------>",res)
onProcess && onProcess("100%");
onComplete && onComplete(res.data);
return
}
this.chunkIndex++;
if (this.chunkIndex < this.totalChunks) {
this.loadNext();
} else {
onProcess && onProcess("100%");
onComplete && onComplete(res.data);
}
}).catch(e =>{
onProcess && onProcess("文件上传失败");
})
};
this.fileReader.onerror =e => {
onProcess && onProcess("文件上传失败");
};
this.loadNext();
}
}
后端代码
表结构
做文件的存储,实际文件存储到Minio中,这里是存储下文件的信息。
CREATE TABLE `MDM_FILE` (
`ID` bigint(65) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`FILE_NAME` varchar(128) DEFAULT NULL COMMENT '文件名称',
`FILE_URL` varchar(512) NOT NULL COMMENT '文件地址',
`FILE_ID` bigint(65) DEFAULT NULL COMMENT '文件id',
`FILE_SIZE` bigint(20) DEFAULT NULL COMMENT '文件大小',
`CREATE_BY` varchar(128) DEFAULT NULL COMMENT '创建者',
`CREATE_TIME` datetime DEFAULT NULL COMMENT '创建时间',
`UPDATE_BY` varchar(128) DEFAULT NULL COMMENT '更新者',
`UPDATE_TIME` datetime DEFAULT NULL COMMENT '更新时间',
`REMARK` varchar(128) DEFAULT NULL COMMENT '备注',
`create_name` varchar(64) DEFAULT NULL COMMENT '创建人',
`UPDATE_NAME` varchar(64) DEFAULT NULL COMMENT '修改人',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=80 DEFAULT CHARSET=utf8 COMMENT='附件表';
实体类
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
* 附件表
* </p>
*
* @author zyl
* @since 2023-12-21
*/
@Getter
@Setter
@TableName("MDM_FILE")
public class MdmFile implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "ID", type = IdType.AUTO)
private Long id;
/**
* 文件名称
*/
private String fileName;
/**
* 文件地址
*/
private String fileUrl;
/**
* 文件id
*/
private Long fileId;
/**
* 文件大小
*/
private Long fileSize;
/**
* 创建者
*/
private String createBy;
/**
* 创建时间
*/
private Date createTime;
private String createName;
private String updateName;
/**
* 更新者
*/
private String updateBy;
/**
* 更新时间
*/
private Date updateTime;
/**
* 备注
*/
private String remark;
}
FileService 接口类
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
/**
* <p>
* 附件表 服务类
* </p>
*
* @author zyl
* @since 2023-12-21
*/
public interface FileService extends IService<MdmFile> {
MdmFile uploadFile(MultipartFile file);
void download(Long id, HttpServletResponse response);
void deleteFile(Long id);
MdmFile uploadBigFile(MultipartFile file, BigFile bigFile);
}
FileServiceImpl
实现本地文件表操作及远程Minio操作。
整体思路,文件表里存的是文件的基础信息。当我们使用附件时,我们业务表存的是这个表的ID,根据这个ID我们可以拿到文件信息,实现文件信息展示,或预览。
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.HashMultimap;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Part;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* <p>
* 附件表 服务实现类
* </p>
*
* @author zyl
* @since 2023-12-21
*/
@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, MdmFile> implements FileService {
@Value("${spring.application.name}")
private String applicationName;
@Autowired
private MinioKit minioKit;
@Autowired
private RedisService redisService;
@Override
public MdmFile uploadFile(MultipartFile file) {
boolean b = minioKit.bucketExists(applicationName);
if(!b){
minioKit.bucketAdd(applicationName);
}
String fileName = file.getOriginalFilename();
long dateTimestamp = System.currentTimeMillis();
String fileNameUpload = fileName+"_"+dateTimestamp;
long size = file.getSize();
try {
String url = minioKit.putObjectUrl(applicationName, fileNameUpload, file);
MdmFile mdmFileEntity = new MdmFile();
mdmFileEntity.setFileUrl(url);
mdmFileEntity.setFileSize(size);
SetEntityUtil.setCreateTimeAndUser(mdmFileEntity);
mdmFileEntity.setFileName(fileName);
mdmFileEntity.setRemark(fileNameUpload);
this.baseMapper.insert(mdmFileEntity);
return mdmFileEntity;
} catch (ServerException e) {
e.printStackTrace();
} catch (InsufficientDataException e) {
e.printStackTrace();
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidResponseException e) {
e.printStackTrace();
} catch (XmlParserException e) {
e.printStackTrace();
} catch (InternalException e) {
e.printStackTrace();
}
return null;
}
@Override
public void download(Long id, HttpServletResponse response) {
MdmFile mdmFile = this.baseMapper.selectById(id);
String fileName = mdmFile.getRemark();
if(StringUtils.isEmpty(fileName)){
fileName = mdmFile.getFileName();
}
minioKit.downloadFile(applicationName,fileName,response);
}
@Override
public void deleteFile(Long id) {
MdmFile mdmFile = this.baseMapper.selectById(id);
String fileName = mdmFile.getRemark();
if(StringUtils.isEmpty(fileName)){
fileName = mdmFile.getFileName();
}
try {
String md5Id = fileName.replace(mdmFile.getFileName(), "");
if(StringUtils.isNotEmpty(md5Id) && md5Id.substring(0,1).equals("-")){
md5Id = md5Id.substring(1);
List<MdmFile> mdmFileList = this.getMdmFile(md5Id);
// 如果有多条数据引用源文件,不删除源文件,只删除关联
if(mdmFileList.size() > 1){
this.baseMapper.deleteById(id);
return;
}
// if(md5Id.length() == 32){
// Long totalChunks = mdmFile.getFileSize() / (1024 * 6 * 1024) + 1;
// removeFile(applicationName, totalChunks.intValue() ,md5Id);
// }
}
removeFile(applicationName,50,"ff9db477d61377e100c334bbf310bf2e");
minioKit.removeObject(applicationName,fileName);
this.baseMapper.deleteById(id);
} catch (Exception e) {
throw new BadRequestException("删除文件失败:"+e.getMessage());
}
}
@Override
public MdmFile uploadBigFile(MultipartFile file, BigFile bigFile) {
MinioClient minioClient = minioKit.getMinioClient();
try {
// 确保 bucket 存在
boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(applicationName).build());
if (!isExist) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(applicationName).build());
}
Integer chunkIndex = bigFile.getChunkIndex();
String md5 = bigFile.getMd5();
if(chunkIndex == 0){
// 校验md5文件是否存在
List<MdmFile> existMd5FileList = this.getMdmFile(md5);
if(!existMd5FileList.isEmpty()){
MdmFile existMd5File = existMd5FileList.get(0);
MdmFile mdmFile = new MdmFile();
mdmFile.setFileUrl(existMd5File.getFileUrl());
mdmFile.setFileSize(existMd5File.getFileSize());
mdmFile.setFileName(existMd5File.getFileName());
mdmFile.setRemark(existMd5File.getRemark());
this.baseMapper.insert(mdmFile);
return mdmFile;
}
}
String fileMd5Name =md5;
String fileName = bigFile.getFileName()+"-"+md5;
Integer totalChunk = bigFile.getTotalChunks();
minioKit.uploadFile(applicationName,fileMd5Name,chunkIndex,file);
if(chunkIndex == totalChunk-1){
List<ComposeSource> sourceObjectList = Stream.iterate(0, i -> ++i)
.limit(bigFile.getTotalChunks())
.map(i -> ComposeSource.builder()
.bucket(applicationName)
.object(fileMd5Name.concat("/").concat(Integer.toString(i)))
.build())
.collect(Collectors.toList());
minioKit.composeFile(applicationName, fileName, sourceObjectList);
// 删除分片文件
removeFile(applicationName,totalChunk,fileName);
MdmFile mdmFile = new MdmFile();
String objectUrl = minioKit.getObjectUrl(applicationName, fileName);
mdmFile.setFileUrl(objectUrl);
mdmFile.setFileSize(bigFile.getTotalSize());
mdmFile.setFileName(bigFile.getFileName());
mdmFile.setRemark(fileName);
this.baseMapper.insert(mdmFile);
return mdmFile;
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("Big File Upload Error occurred: " + e.getMessage());
}
return null;
}
private void removeFile(String applicationName,Integer totalChunk, String fileName ) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, XmlParserException, InvalidResponseException, InternalException {
for(int i = 0 ; i < totalChunk; i++){
String objectName =fileName.concat("/").concat(Integer.toString(i));
minioKit.removeObject(applicationName,objectName);
}
// minioKit.removeObject(applicationName,fileName);
}
private List<MdmFile> getMdmFile(String md5){
QueryWrapper<MdmFile> queryWrapper = new QueryWrapper<>();
queryWrapper.like("remark",md5);
return this.baseMapper.selectList(queryWrapper);
}
}
FileMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 附件表 Mapper 接口
* </p>
*
* @author zyl
* @since 2023-12-21
*/
public interface FileMapper extends BaseMapper<MdmFile> {
}
FileUploadController
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.UploadPartResponse;
import io.minio.errors.MinioException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* 附件表 前端控制器
* </p>
*
* @author zyl
* @since 2023-12-21
*/
@RestController
@RequestMapping(value = "/file", name = "文件附件")
public class FileController extends BaseController<MdmFile> {
private FileService fileService;
@Autowired
private RedisService redisCache;
@Autowired
public FileController(FileService fileService){
this.baseService =fileService;
this.fileService=fileService;
}
@PostMapping("/upload")
@AuditBizLog(desc = "附件上传", businessType = BusinessType.INSERT)
public ResponseEntity<MdmFile> uploadFile(@RequestParam("file") MultipartFile file){
MdmFile mdmFileEntity = this.fileService.uploadFile(file);
return new ResponseEntity<>(mdmFileEntity, HttpStatus.OK);
}
@PostMapping("/uploadBigFile")
public ResponseEntity<MdmFile> uploadBigFile(@RequestParam("chunk") MultipartFile file, @ModelAttribute BigFile bigFile){
MdmFile mdmFileEntity = this.fileService.uploadBigFile(file,bigFile);
return new ResponseEntity<>(mdmFileEntity, HttpStatus.OK);
}
@GetMapping("/getFiles")
public ResponseEntity<List<MdmFile>> getFiles(String ids){
List<MdmFile> mdmFiles = new ArrayList<>();
if(StringUtils.isEmpty(ids)) {
return new ResponseEntity<>(mdmFiles,HttpStatus.OK);
}
String[] idArr = ids.split(",");
for(String id : idArr){
mdmFiles.add(this.fileService.getById(id));
}
return new ResponseEntity<>(mdmFiles,HttpStatus.OK);
}
@GetMapping("/download")
public void download( Long id, HttpServletResponse response) {
this.fileService.download(id,response);
}
@DeleteMapping("/deleteFile")
@AuditBizLog(desc = "附件删除", businessType = BusinessType.DELETE)
public void deleteFile(Long id){
this.fileService.deleteFile(id);
}
@GetMapping("/getUploadInfo")
public ResponseEntity<String> getUploadInfo(Long timestamp){
Object msg = redisCache.getCacheObject(RedisConstant.UPLOAD_EXCEL_FILE + timestamp);
if(msg==null){
return new ResponseEntity<>("",HttpStatus.OK);
}
return new ResponseEntity<>(msg.toString(),HttpStatus.OK);
}
}
Minio工具类增加方法
/**
* 文件上传/文件分块上传
*
* @param bucketName 桶名称
* @param objectName 对象名称
* @param sliceIndex 分片索引
* @param file 文件
*/
public Boolean uploadFile(String bucketName, String objectName, Integer sliceIndex, MultipartFile file) {
try {
if (sliceIndex != null) {
objectName = objectName.concat("/").concat(Integer.toString(sliceIndex));
}
// 写入文件
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build());
log.debug("上传到minio文件|uploadFile|参数:bucketName:{},objectName:{},sliceIndex:{}"
, bucketName, objectName, sliceIndex);
return true;
} catch (Exception e) {
log.error("文件上传到Minio异常|参数:bucketName:{},objectName:{},sliceIndex:{}|异常:{}", bucketName, objectName, sliceIndex, e);
return false;
}
}
/**
* 文件合并
*
* @param bucketName 桶名称
* @param objectName 对象名称
* @param sourceObjectList 源文件分片数据
*/
public Boolean composeFile(String bucketName, String objectName, List<ComposeSource> sourceObjectList) {
// 合并操作
try {
ObjectWriteResponse response = minioClient.composeObject(
ComposeObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.sources(sourceObjectList)
.build());
return true;
} catch (Exception e) {
e.printStackTrace();
log.error("组合文件失败:"+objectName);
return false;
}
}