当前位置:首页 » 《随便一记》 » 正文

springboot整合Minio + vue 实现文件分片上传(完整代码)

24 人参与  2023年05月04日 10:49  分类 : 《随便一记》  评论

点击全文阅读


网上关于minio分片上传的资料不太详细,缺斤少两,所以我基于他们的代码做了一些修改,demo能够正常运行起来,但是偶尔也会发生一些小bug,不过这些都无伤大雅,最终目的是理解代码背后的逻辑和流程

流程:

前端获取生成文件MD5,发送至后台判断是否有该文件缓存,有信息终止上传,无则开始进行文件分片  。这里,我为了简单方便实现便没有使用数据库,直接用redis存储文件信息;前端后端返回的结果进行分片,然后将文件分片的信息传输给后端,后端调用 minio 初始化,返回分片上传地址和 uploadId;前端则根据获取的分片上传地址直接通过axios上传分片文件,不走后端;上传完成后,前端发送请求至后端,后端调用 minio 合并文件;

流程图:

效果图

  1.vue前端

2. minio文件桶

一.前端vue代码(代码较多,我就分开贴)

 项目中使用到的类库:spark-md5axioselement-ui

spark-md5 主要用来计算文件MD5,安装命令:

npm install spark-md5 --S

   1.template 

<template><div class="container"><div style="display:none;"><video width="320" height="240" controls id="upvideo"></video></div><h2>上传示例</h2><el-upload class="upload-demo" ref="upload" action="https://jsonplaceholder.typicode.com/posts/":on-remove="handleRemove" :on-change="handleFileChange" :file-list="uploadFileList" :show-file-list="false":auto-upload="false" multiple><el-button slot="trigger" type="primary" plain>选择文件</el-button><el-button style="margin-left: 5px;" type="success" @click="handler" plain>上传</el-button><el-button type="danger" @click="clearFileHandler" plain>清空</el-button></el-upload><img :src="imgDataUrl" v-show="imgDataUrl" /><!-- 文件列表 --><div class="file-list-wrapper"><el-collapse><el-collapse-item v-for="(item, index) in uploadFileList" :key="index"><template slot="title"><div class="upload-file-item"><div class="file-info-item file-name" :title="item.name">{{ item.name }}</div><div class="file-info-item file-size">{{ item.size | transformByte }}</div><div class="file-info-item file-progress"><span class="file-progress-label"></span><el-progress :percentage="item.uploadProgress" class="file-progress-value" /></div><div class="file-info-item file-size"><span></span><el-tag v-if="item.status === '等待上传'" size="medium" type="info">等待上传</el-tag><el-tag v-else-if="item.status === '校验MD5'" size="medium" type="warning">校验MD5</el-tag><el-tag v-else-if="item.status === '正在上传'" size="medium">正在上传</el-tag><el-tag v-else-if="item.status === '上传成功'" size="medium" type="success">上传完成</el-tag><el-tag v-else size="medium" type="danger">上传错误</el-tag></div></div></template><div class="file-chunk-list-wrapper"><!-- 分片列表 --><el-table :data="item.chunkList" max-height="400" style="width: 100%"><el-table-column prop="chunkNumber" label="分片序号" width="180"></el-table-column><el-table-column prop="progress" label="上传进度"><template v-slot="{ row }"><el-progress v-if="!row.status || row.progressStatus === 'normal'":percentage="row.progress" /><el-progress v-else :percentage="row.progress" :status="row.progressStatus":text-inside="true" :stroke-width="16" /></template></el-table-column><el-table-column prop="status" label="状态" width="180"></el-table-column></el-table></div></el-collapse-item></el-collapse></div></div></template>

2.scirpt

<script>import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'import { checkUpload, initUpload, mergeUpload, uploadFileInfo } from "@/api/upload";import {fileSuffixTypeUtil} from "@/utils/util"import SparkMD5 from 'spark-md5'const FILE_UPLOAD_ID_KEY = 'file_upload_id'const chunkSize = 10 * 1024 * 1024let currentFileIndex = 0const FileStatus = {wait: '等待上传',getMd5: '校验MD5',chip: '正在创建序列',uploading: '正在上传',success: '上传成功',error: '上传错误'}export default {data() {return {changeDisabled: false,uploadDisabled: false,// 上传并发数simultaneousUploads: 3,uploadIdInfo: null,uploadFileList: [],retryList: [],videoPoster: null,ffmpeg: createFFmpeg({ log: true }),  //截图工具ffmpegimgDataUrl: '',}},methods: {/** * 开始上传文件 */handler() {const self = this//判断文件列表是否为空if (this.uploadFileList.length === 0) {this.$message.error('请先选择文件')return}//当前操作文件const currentFile = this.uploadFileList[currentFileIndex]//更新上传标签currentFile.status = FileStatus.getMd5//截取封面图片//this.ScreenshotVideo(currentFile.raw);// 1. 计算文件MD5this.getFileMd5(currentFile.raw, async (md5, totalChunks) => {// 2. 检查是否已上传const checkResult = await self.checkFileUploadedByMd5(md5)// 确认上传状态if (checkResult.code === 1) {self.$message.success(`上传成功,文件地址:${checkResult.data.url}`)console.log('文件访问地址:' + checkResult.data.url)currentFile.status = FileStatus.successcurrentFile.uploadProgress = 100return} else if (checkResult.code === 2) {  // "上传中" 状态// 获取已上传分片列表let chunkUploadedList = checkResult.datacurrentFile.chunkUploadedList = chunkUploadedList} else {   // 未上传console.log('未上传')}// 3. 正在创建分片//currentFile.status = FileStatus.chip;//创建分片let fileChunks = self.createFileChunk(currentFile.raw, chunkSize);//重命名文件let fileName = this.getNewFileName(currentFile)// 获取文件类型//let type = currentFile.name.substring(currentFile.name.lastIndexOf(".") + 1)let type = fileSuffixTypeUtil(currentFile.name)let param = {fileName: fileName,fileSize: currentFile.size,chunkSize: chunkSize,partCount: totalChunks,fileMd5: md5,contentType: 'application/octet-stream',fileType: type,}// 4. 获取上传urllet uploadIdInfoResult = await self.getFileUploadUrls(param)debuggerlet uploadIdInfo = uploadIdInfoResult.data.dataself.saveFileUploadId(uploadIdInfo.uploadId)let uploadUrls = uploadIdInfo.urlListself.$set(currentFile, 'chunkList', [])if (uploadUrls !== undefined) {if (fileChunks.length !== uploadUrls.length) {self.$message.error('文件分片上传地址获取错误')return}}// else if (uploadUrls.length === 1) {// currentFileIndex++;// //文件上传成功// //this.saveFileInfoToDB(currentFile, fileName, uploadIdInfoResult.data.data, md5);// currentFile.uploadProgress = 100// currentFile.status = FileStatus.success// //return;// }fileChunks.map((chunkItem, index) => {currentFile.chunkList.push({chunkNumber: index + 1,chunk: chunkItem,uploadUrl: uploadUrls[index],progress: 0,status: '—'})})let tempFileChunks = []currentFile.chunkList.forEach((item) => {tempFileChunks.push(item)})//更新状态currentFile.status = FileStatus.uploading// 5. 上传await self.uploadChunkBase(tempFileChunks)// let imgParam = {// fileName: screenImg.name,// fileSize: screenImg.size,// partCount: 1,// contentType: 'application/octet-stream',// fileType: 'image',// }// //上传封面图// let screenImgUrl = await self.getFileUploadUrls(imgParam)// 处理分片列表,删除已上传的分片tempFileChunks = self.processUploadChunkList(tempFileChunks)console.log('上传完成')//判断是否单文件上传或者分片上传if (uploadIdInfo.uploadId === "SingleFileUpload") {console.log("单文件上传");//更新状态currentFile.status = FileStatus.success}else {// 6. 合并文件const mergeResult = await self.mergeFile({uploadId: uploadIdInfo.uploadId,fileName: fileName,fileMd5: md5,fileType: type,})//合并文件状态if (!mergeResult.data) {currentFile.status = FileStatus.errorself.$message.error(mergeResult.error)} else {currentFile.status = FileStatus.successconsole.log('文件访问地址:' + mergeResult.data)self.$message.success(`上传成功,文件地址:${mergeResult.data}`)//文件下标偏移                        currentFileIndex++;                        //递归上传下一个文件                        this.handler()}}//this.saveFileInfoToDB( currentFile, fileName, mergeResult.url, md5);})},/** * 保存文件信息到数据库 * @param {*} imgInfoUrl  上传图片封面 * @param {*} currentFile 上传文件 * @param {*} fileName 文件名 * @param {*} url 文件url地址 * @param {*} md5 md5校验 */saveFileInfoToDB(currentFile, fileName, url, md5) {let userInfoCache = JSON.parse(localStorage.getItem('userInfo'))let VideoFileInfo = {userId: userInfoCache.id,fileRealName: currentFile.name,fileName: fileName,fileSize: currentFile.size,fileMd5: md5,fileAddress: url,imgAddress: imgInfoUrl,bucketName: 'video',fileType: 'video',}console.log(VideoFileInfo);uploadFileInfo(VideoFileInfo).then(res => {console.log(res.data);if (res.status == 200) {this.$message.success("文件信息存储成功");//递归上传文件if (this.uploadFileList.length > currentFileIndex) {this.handleUpload()}} else {this.$message.error("文件信息存储失败");}})},/** * 清空列表 */clearFileHandler() {this.uploadFileList = []this.uploadIdInfo = nullcurrentFileIndex = 0},/** * 上传文件列表 * @param {*} file  * @param {*} fileList  */handleFileChange(file, fileList) {//if (!this.beforeUploadVideo(file)) returnthis.uploadFileList = fileListthis.uploadFileList.forEach((item) => {// 初始化自定义属性this.initFileProperties(item)})},//初始化文件属性initFileProperties(file) {file.chunkList = []file.status = FileStatus.waitfile.progressStatus = 'warning'file.uploadProgress = 0},/** * 移除文件列表 * @param {*} file  * @param {*} fileList  */handleRemove(file, fileList) {this.uploadFileList = fileList},/** * 检查上传文件格式 * @param {*} file  */beforeUploadVideo(file) {let type = file.name.substring(file.name.lastIndexOf(".") + 1);if (["mp4","ogg","flv","avi","wmv","rmvb"].indexOf(type) == -1) {this.$message.error("请上传正确的视频格式");return false;}},getNewFileName(file,md5) {return new Date().getTime() + file.name//return md5+"-"+ file.name},/** * 分片读取文件 MD5 */getFileMd5(file, callback) {const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSliceconst fileReader = new FileReader()// 计算分片数const totalChunks = Math.ceil(file.size / chunkSize)console.log('总分片数:' + totalChunks)let currentChunk = 0const spark = new SparkMD5.ArrayBuffer()loadNext()fileReader.onload = function (e) {try {spark.append(e.target.result)} catch (error) {console.log('获取Md5错误:' + currentChunk)}if (currentChunk < totalChunks) {currentChunk++loadNext()} else {callback(spark.end(), totalChunks)}}fileReader.onerror = function () {console.warn('读取Md5失败,文件读取错误')}function loadNext() {const start = currentChunk * chunkSizeconst end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize// 注意这里的 fileRawfileReader.readAsArrayBuffer(blobSlice.call(file, start, end))}},/** * 文件分片 */createFileChunk(file, size = chunkSize) {const fileChunkList = []let count = 0while (count < file.size) {fileChunkList.push({file: file.slice(count, count + size),})count += size}return fileChunkList},/** * 处理即将上传的分片列表,判断是否有已上传的分片,有则从列表中删除 */processUploadChunkList(chunkList) {const currentFile = this.uploadFileList[currentFileIndex]let chunkUploadedList = currentFile.chunkUploadedListif (chunkUploadedList === undefined || chunkUploadedList === null || chunkUploadedList.length === 0) {return chunkList}// for (let i = chunkList.length - 1; i >= 0; i--) {const chunkItem = chunkList[currentFileIndex]for (let j = 0; j < chunkUploadedList.length; j++) {if (chunkItem.chunkNumber === chunkUploadedList[j]) {chunkList.splice(i, 1)break}}}return chunkList},uploadChunkBase(chunkList) {const self = thislet successCount = 0let totalChunks = chunkList.lengthreturn new Promise((resolve, reject) => {const handler = () => {if (chunkList.length) {const chunkItem = chunkList.shift()// 直接上传二进制,不需要构造 FormData,否则上传后文件损坏axios.put(chunkItem.uploadUrl, chunkItem.chunk.file, {// 上传进度处理onUploadProgress: self.checkChunkUploadProgress(chunkItem),headers: {'Content-Type': 'application/octet-stream'}}).then(response => {if (response.status === 200) {console.log('分片:' + chunkItem.chunkNumber + ' 上传成功')//如果长度为1,说明是单文件,直接退出// if (chunkList.length === 1) {// return;// }successCount++// 继续上传下一个分片handler()} else {console.log('上传失败:' + response.status + ',' + response.statusText)}}).catch(error => {// 更新状态console.log('分片:' + chunkItem.chunkNumber + ' 上传失败,' + error)// 重新添加到队列中chunkList.push(chunkItem)handler()})}if (successCount >= totalChunks) {resolve()}}// 并发for (let i = 0; i < this.simultaneousUploads; i++) {handler()}})},getFileUploadUrls(fileParam) {return initUpload(fileParam)},saveFileUploadId(data) {localStorage.setItem(FILE_UPLOAD_ID_KEY, data)},checkFileUploadedByMd5(md5) {return new Promise((resolve, reject) => {checkUpload(md5).then(response => {console.log(response.data);resolve(response.data)}).catch(error => {reject(error)})})},/** * 合并文件 *   uploadId: self.uploadIdInfo.uploadId,fileName: currentFile.name,//fileMd5: fileMd5,bucketName: 'bucket' */mergeFile(fileParam) {const self = this;return new Promise((resolve, reject) => {mergeUpload(fileParam).then(response => {console.log(response.data);let data = response.dataif (!data.data) {data.msg = FileStatus.errorresolve(data)} else {data.msg = FileStatus.successresolve(data)}})// .catch(error => {//     self.$message.error('合并文件失败:' + error)//     file.status = FileStatus.error//     reject()// })})},/** * 检查分片上传进度 */checkChunkUploadProgress(item) {return p => {item.progress = parseInt(String((p.loaded / p.total) * 100))this.updateChunkUploadStatus(item)}},updateChunkUploadStatus(item) {let status = FileStatus.uploadinglet progressStatus = 'normal'if (item.progress >= 100) {status = FileStatus.successprogressStatus = 'success'}let chunkIndex = item.chunkNumber - 1let currentChunk = this.uploadFileList[currentFileIndex].chunkList[chunkIndex]// 修改状态currentChunk.status = statuscurrentChunk.progressStatus = progressStatus// 更新状态this.$set(this.uploadFileList[currentFileIndex].chunkList, chunkIndex, currentChunk)// 获取文件上传进度this.getCurrentFileProgress()},getCurrentFileProgress() {const currentFile = this.uploadFileList[currentFileIndex]if (!currentFile || !currentFile.chunkList) {return}const chunkList = currentFile.chunkListconst uploadedSize = chunkList.map((item) => item.chunk.file.size * item.progress).reduce((acc, cur) => acc + cur)// 计算方式:已上传大小 / 文件总大小let progress = parseInt((uploadedSize / currentFile.size).toFixed(2))currentFile.uploadProgress = progressthis.$set(this.uploadFileList, currentFile)},},filters: {transformByte(size) {if (!size) {return '0B'}const unitSize = 1024if (size < unitSize) {return size + ' B'}// KBif (size < Math.pow(unitSize, 2)) {return (size / unitSize).toFixed(2) + ' K';}// MBif (size < Math.pow(unitSize, 3)) {return (size / Math.pow(unitSize, 2)).toFixed(2) + ' MB'}// GBif (size < Math.pow(unitSize, 4)) {return (size / Math.pow(unitSize, 3)).toFixed(2) + ' GB';}// TBreturn (size / Math.pow(unitSize, 4)).toFixed(2) + ' TB';}}}</script>

3.css

<style scoped lang="less">.container {width: 600px;margin: 0 auto;}.file-list-wrapper {margin-top: 20px;}h2 {text-align: center;}.file-info-item {margin: 0 10px;}.upload-file-item {display: flex;}.file-progress {display: flex;align-items: center;}.file-progress-value {width: 150px;}.file-name {width: 190px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}.file-size {width: 100px;}.uploader-example {width: 880px;padding: 15px;margin: 40px auto 0;font-size: 12px;box-shadow: 0 0 10px rgba(0, 0, 0, .4);}.uploader-example .uploader-btn {margin-right: 4px;}.uploader-example .uploader-list {max-height: 440px;overflow: auto;overflow-x: hidden;overflow-y: auto;}</style>

4.upload.js

import request from '@/utils/request'//上传信息export function uploadScreenshot(data){    return request({        url:'upload/multipart/uploadScreenshot',        method:'post',        data    })}//上传信息export function uploadFileInfo(data){    return request({        url:'upload/multipart/uploadFileInfo',        method:'post',        data    })}// 上传校验export function checkUpload(MD5) {    return request({        url: `upload/multipart/check?md5=${MD5}`,        method: 'get',    })};// 初始化上传export function initUpload(data) {    return request({        url: `upload/multipart/init`,        method: 'post',        data    })};// 初始化上传export function mergeUpload(data) {    return request({        url: `upload/multipart/merge`,        method: 'post',        data    })};

5.request.js

import axios from 'axios'import { getToken } from '@/utils/CookiesSet' //这个是获取token值,获取即可//import Qs from 'qs' //如果需要转换// 创建 axios 实例const service = axios.create({  baseURL: "/api", // 环境的不同,对应不同的baseURL  // transformRequest: [function(data) {  //   return Qs.stringify(data)  // }],  //timeout: 5000 // 请求超时时间})//request请求拦截service.interceptors.request.use(  config => {    var token=getToken()    if (token) {        config.headers.token = token // 让每个请求携带自定义token 请根据实际情况自行修改      }    return config;},  error => {    // do something with request error    return Promise.reject(error)  })//响应拦截service.interceptors.response.use(  response => {    const res = response    if (res.data.status !== 200) { //code返回参数根据实际后端返回参数    }    return res  },  error => {        //这里还可以根据实际情况增加一些功能    return Promise.reject(error)  })export default service

二.后端代码

后端使用的是springboot ,使用之前要启动minio,redis,否则文件上传会出现异常。这里我都是使用windows版的

1.controller,文件上传接口

package com.xy.controller;import com.xy.entity.FileInfo;import com.xy.entity.FileUploadInfo;import com.xy.service.UploadService;import com.xy.service.VideoFileInfoService;import com.xy.util.MinioUtils;import com.xy.util.ResponseResult;import lombok.extern.slf4j.Slf4j;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.*;import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;import static com.xy.util.ResponseResult.error;import static com.xy.util.ResponseResult.success;import static com.xy.util.ResultCode.ACCESS_PARAMETER_INVALID;/** * minio上传流程 * * 1.检查数据库中是否存在上传文件 * * 2.根据文件信息初始化,获取分片预签名url地址,前端根据url地址上传文件 * * 3.上传完成后,将分片上传的文件进行合并 * * 4.保存文件信息到数据库 */@RestController@Slf4jpublic class FileMinioController {    @Resource    private UploadService uploadService;    @Resource    private VideoFileInfoService videoFileInfoService;    @Resource    private MinioUtils minioUtils;    /**     * 校验文件是否存在     *     * @param md5 String     * @return ResponseResult<Object>     */    @GetMapping("/multipart/check")    public ResponseResult checkFileUploadedByMd5(@RequestParam("md5") String md5) {        log.info("REST: 通过查询 <{}> 文件是否存在、是否进行断点续传", md5);        if (StringUtils.isEmpty(md5)) {            log.error("查询文件是否存在、入参无效");            return error(ACCESS_PARAMETER_INVALID);        }        return uploadService.getByFileSha256(md5);    }    /**     * 分片初始化     *     * @param fileUploadInfo 文件信息     * @return ResponseResult<Object>     */    @PostMapping("/multipart/init")    public ResponseResult initMultiPartUpload(@RequestBody FileUploadInfo fileUploadInfo) {        log.info("REST: 通过 <{}> 初始化上传任务", fileUploadInfo);        return uploadService.initMultiPartUpload(fileUploadInfo);    }    /**     * 完成上传     *     * @param fileUploadInfo  文件信息     * @return ResponseResult<Object>     */    @PostMapping("/multipart/merge")    public ResponseResult completeMultiPartUpload(@RequestBody FileUploadInfo fileUploadInfo) {        log.info("REST: 通过 {} 合并上传任务", fileUploadInfo);        //Map<String, Object> resMap = new HashMap<>();        //合并文件        boolean result = uploadService.mergeMultipartUpload(fileUploadInfo);        //获取上传文件地址        if(result){            String fliePath = uploadService.getFliePath(fileUploadInfo.getFileType().toLowerCase(), fileUploadInfo.getFileName());            return success(fliePath);        }        return error();    }    /**     * 保存文件信息到数据库     * @param fileInfo 文件信息     * @return     */    @PostMapping("/multipart/uploadFileInfo")    public ResponseResult uploadFileInfo(@RequestBody FileInfo fileInfo){        log.info("REST: 上传文件信息 <{}> ", fileInfo);        if(fileInfo ==null){           return error(ACCESS_PARAMETER_INVALID);        }else{            FileInfo insert = videoFileInfoService.insert(fileInfo);        }        return success();    }    @PostMapping("/multipart/uploadScreenshot")    public ResponseResult uploaduploadScreenshot(@RequestPart("photos") MultipartFile[] photos,                                                 @RequestParam("buckName") String buckName){        log.info("REST: 上传文件信息 <{}> ", photos);        for (MultipartFile photo : photos) {            if (!photo.isEmpty()) {                uploadService.upload(photo,buckName);            }        }        return success();    }    @RequestMapping("/createBucket")    public void createBucket(@RequestParam("bucketName")String bucketName){        String bucket = minioUtils.createBucket(bucketName);    }    }

2.UploadService

package com.xy.service;import com.xy.entity.FileUploadInfo;import com.xy.util.ResponseResult;import org.springframework.web.multipart.MultipartFile;public interface UploadService {    /**     * 分片上传初始化     *     * @param fileUploadInfo     * @return Map<String, Object>     */    ResponseResult<Object> initMultiPartUpload(FileUploadInfo fileUploadInfo);    /**     * 完成分片上传     *     * @param  fileUploadInfo     * @return boolean     */    boolean mergeMultipartUpload(FileUploadInfo fileUploadInfo);    /**     *  通过 sha256 获取已上传的数据     * @param sha256 String     * @return Mono<Map<String, Object>>     */    ResponseResult<Object> getByFileSha256(String sha256);    /**     *  获取文件地址     * @param bucketName     * @param fileName     *     */    String getFliePath(String bucketName, String fileName);    /**     * 单文件上传     * @param file     * @param bucketName     * @return     */    String upload(MultipartFile file, String bucketName);}

3.UploadServiceImpl

package com.xy.service.impl;import com.alibaba.fastjson.JSONObject;import com.xy.entity.FileUploadInfo;import com.xy.service.UploadService;import com.xy.util.MinioUtils;import com.xy.util.RedisRepo;import com.xy.util.ResponseResult;import com.xy.util.ResultCode;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;@Slf4j@Servicepublic class UploadServiceImpl implements UploadService {    @Resource    private MinioUtils fileService;    @Resource    private RedisRepo redisRepo;    /**     * 通过 sha256 获取已上传的数据(断点续传)     *     * @param sha256 String     * @return Mono<Map < String, Object>>     */    @Override    public ResponseResult<Object> getByFileSha256(String sha256) {        log.info("tip message: 通过 <{}> 查询数据是否存在", sha256);        // 获取文件名称和id        String value = redisRepo.get(sha256);        FileUploadInfo fileUploadInfo = null;        if (value != null) {            fileUploadInfo = JSONObject.parseObject(value, FileUploadInfo.class);        }        if (fileUploadInfo == null) {            // 返回数据不存在            log.error("error message: 文件数据不存在");            return ResponseResult.error(ResultCode.FOUND);        }        // 获取桶名称        String bucketName = fileService.getBucketName(fileUploadInfo.getFileType());        return fileService.getByFileSha256(fileUploadInfo.getFileName(), fileUploadInfo.getUploadId(), bucketName);    }    /**     * 文件分片上传     *     * @param fileUploadInfo     * @return Mono<Map < String, Object>>     */    @Override    public ResponseResult<Object> initMultiPartUpload(FileUploadInfo fileUploadInfo) {        log.info("tip message: 通过 <{}> 开始初始化<分片上传>任务", fileUploadInfo);        // 获取桶        String bucketName = fileService.getBucketName(fileUploadInfo.getFileType());        // 单文件上传可拆分,这里只做演示,可直接上传完成        if (fileUploadInfo.getPartCount() == 1) {            log.info("tip message: 当前分片数量 <{}> 进行单文件上传", fileUploadInfo.getPartCount());            return fileService.getUploadObjectUrl(fileUploadInfo.getFileName(), bucketName);        }        // 分片上传        else {            log.info("tip message: 当前分片数量 <{}> 进行分片上传", fileUploadInfo.getPartCount());            return fileService.initMultiPartUpload(fileUploadInfo, fileUploadInfo.getFileName(), fileUploadInfo.getPartCount(), fileUploadInfo.getContentType(), bucketName);        }    }    /**     * 文件合并     *     * @param     * @return boolean     */    @Override    public boolean mergeMultipartUpload(FileUploadInfo fileUploadInfo) {        log.info("tip message: 通过 <{}> 开始合并<分片上传>任务", fileUploadInfo);        // 获取桶名称        String bucketName = fileService.getBucketName(fileUploadInfo.getFileType());        return fileService.mergeMultipartUpload(fileUploadInfo.getFileName(), fileUploadInfo.getUploadId(), bucketName);    }    @Override    public String getFliePath(String bucketName, String fileName) {        return fileService.getFliePath(bucketName, fileName);    }    @Override    public String upload(MultipartFile file, String bucketName) {        fileService.upload(file, bucketName);        return getFliePath(bucketName, file.getName());    }}

4.MinioUtils

package com.xy.util;import cn.hutool.core.text.CharSequenceUtil;import cn.hutool.core.util.StrUtil;import com.alibaba.fastjson.JSONObject;import com.google.common.collect.HashMultimap;import com.xy.config.CustomMinioClient;import com.xy.entity.FileUploadInfo;import io.minio.*;import io.minio.errors.*;import io.minio.http.Method;import io.minio.messages.Part;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.StringUtils;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import org.springframework.web.multipart.MultipartFile;import javax.annotation.PostConstruct;import javax.annotation.Resource;import java.io.IOException;import java.io.InputStream;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.util.*;import java.util.concurrent.TimeUnit;import java.util.stream.Collectors;import static com.xy.util.ResultCode.DATA_NOT_EXISTS;import static com.xy.util.ResultCode.UPLOAD_FILE_FAILED;@Slf4j@Componentpublic class MinioUtils {    @Value(value = "${minio.endpoint}")    private String endpoint;    @Value(value = "${minio.accesskey}")    private String accesskey;    @Value(value = "${minio.secretkey}")    private String secretkey;    @Resource    private RedisRepo redisRepo;    private CustomMinioClient customMinioClient;    //初始化配置文件    private Properties SysLocalPropObject = new Properties();    /**     * 用spring的自动注入会注入失败     */    @PostConstruct    public void init() {        MinioClient minioClient = MinioClient.builder()                .endpoint(endpoint)                .credentials(accesskey, secretkey)                .build();        customMinioClient = new CustomMinioClient(minioClient);    }    /**     * 单文件签名上传     *     * @param objectName 文件全路径名称     * @param bucketName 桶名称     * @return /     */    public ResponseResult<Object> getUploadObjectUrl(String objectName, String bucketName) {        try {            log.info("tip message: 通过 <{}-{}> 开始单文件上传<minio>", objectName, bucketName);            Map<String, Object> resMap = new HashMap<>();            List<String> partList = new ArrayList<>();            String url = customMinioClient.getPresignedObjectUrl(                    GetPresignedObjectUrlArgs.builder()                            .method(Method.PUT)                            .bucket(bucketName)                            .object(objectName)                            .expiry(1, TimeUnit.DAYS)                            .build());            log.info("tip message: 单个文件上传、成功");            partList.add(url);            resMap.put("uploadId", "SingleFileUpload");            resMap.put("urlList", partList);            return ResponseResult.success(resMap);        } catch (Exception e) {            log.error("error message: 单个文件上传失败、原因:", e);            // 返回 文件上传失败            return ResponseResult.error(UPLOAD_FILE_FAILED);        }    }    /**     * 初始化分片上传     *     * @param fileUploadInfo     * @param objectName     文件全路径名称     * @param partCount      分片数量     * @param contentType    类型,如果类型使用默认流会导致无法预览     * @param bucketName     桶名称     * @return Mono<Map < String, Object>>     */    public ResponseResult<Object> initMultiPartUpload(FileUploadInfo fileUploadInfo, String objectName, int partCount, String contentType, String bucketName) {        log.info("tip message: 通过 <{}-{}-{}-{}> 开始初始化<分片上传>数据", objectName, partCount, contentType, bucketName);        Map<String, Object> resMap = new HashMap<>();        try {            if (CharSequenceUtil.isBlank(contentType)) {                contentType = "application/octet-stream";            }            HashMultimap<String, String> headers = HashMultimap.create();            headers.put("Content-Type", contentType);            //获取uploadId            String uploadId = customMinioClient.initMultiPartUpload(bucketName, null, objectName, headers, null);            resMap.put("uploadId", uploadId);            fileUploadInfo.setUploadId(uploadId);            //redis保存文件信息            redisRepo.saveTimeout(fileUploadInfo.getFileMd5(), JSONObject.toJSONString(fileUploadInfo), 30, TimeUnit.MINUTES);            List<String> partList = new ArrayList<>();            Map<String, String> reqParams = new HashMap<>();            reqParams.put("uploadId", uploadId);            for (int i = 1; i <= partCount; i++) {                reqParams.put("partNumber", String.valueOf(i));                String uploadUrl = customMinioClient.getPresignedObjectUrl(                        GetPresignedObjectUrlArgs.builder()                                .method(Method.PUT)                                .bucket(bucketName)                                .object(objectName)                                .expiry(1, TimeUnit.DAYS)                                .extraQueryParams(reqParams)                                .build());                partList.add(uploadUrl);            }            log.info("tip message: 文件初始化<分片上传>、成功");            resMap.put("urlList", partList);            return ResponseResult.success(resMap);        } catch (Exception e) {            log.error("error message: 初始化分片上传失败、原因:", e);            // 返回 文件上传失败            return ResponseResult.error(UPLOAD_FILE_FAILED);        }    }    /**     * 分片上传完后合并     *     * @param objectName 文件全路径名称     * @param uploadId   返回的uploadId     * @param bucketName 桶名称     * @return boolean     */    public boolean mergeMultipartUpload(String objectName, String uploadId, String bucketName) {        try {            log.info("tip message: 通过 <{}-{}-{}> 合并<分片上传>数据", objectName, uploadId, bucketName);            //目前仅做了最大1000分片            Part[] parts = new Part[1000];            // 查询上传后的分片数据            ListPartsResponse partResult = customMinioClient.listMultipart(bucketName, null, objectName, 1000, 0, uploadId, null, null);            int partNumber = 1;            for (Part part : partResult.result().partList()) {                parts[partNumber - 1] = new Part(partNumber, part.etag());                partNumber++;            }            // 合并分片            customMinioClient.mergeMultipartUpload(bucketName, null, objectName, uploadId, parts, null, null);        } catch (Exception e) {            log.error("error message: 合并失败、原因:", e);            return false;        }        return true;    }    /**     * 通过 sha256 获取上传中的分片信息     *     * @param objectName 文件全路径名称     * @param uploadId   返回的uploadId     * @param bucketName 桶名称     * @return Mono<Map < String, Object>>     */    public ResponseResult<Object> getByFileSha256(String objectName, String uploadId, String bucketName) {        log.info("通过 <{}-{}-{}> 查询<minio>上传分片数据", objectName, uploadId, bucketName);        //Map<String, Object> resMap = new HashMap<>();        try {            // 查询上传后的分片数据            ListPartsResponse partResult = customMinioClient.listMultipart(bucketName, null, objectName, 1000, 0, uploadId, null, null);            List<Integer> collect = partResult.result().partList().stream().map(Part::partNumber).collect(Collectors.toList());//            resMap.put(SystemEnumEntity.ApiRes.CODE.getValue(), SystemErrorCode.DATA_UPDATE_SUCCESS.getCode());//            resMap.put(SystemEnumEntity.ApiRes.MESSAGE.getValue(), SystemErrorCode.DATA_UPDATE_SUCCESS.getMsg());//            resMap.put(SystemEnumEntity.ApiRes.COUNT.getValue(), collect);            return ResponseResult.uploading(collect);        } catch (Exception e) {            log.error("error message: 查询上传后的分片信息失败、原因:", e);            return ResponseResult.error(DATA_NOT_EXISTS);        }    }    /**     * 获取文件下载地址     *     * @param bucketName 桶名称     * @param fileName   文件名     * @return     */    public String getFliePath(String bucketName, String fileName) {        return StrUtil.format("{}/{}/{}", endpoint, bucketName, fileName);//文件访问路径    }    /**     * 创建一个桶     *     * @return     */    public String createBucket(String bucketName) {        try {            BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucketName).build();            //如果桶存在            if (customMinioClient.bucketExists(bucketExistsArgs)) {                return bucketName;            }            MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();            customMinioClient.makeBucket(makeBucketArgs);            return bucketName;        } catch (Exception e) {            log.error("创建桶失败:{}", e.getMessage());            throw new RuntimeException(e);        }    }    /**     * 根据文件类型获取minio桶名称     *     * @param fileType     * @return     */    public String getBucketName(String fileType) {        try {            //String bucketName = getProperty(fileType.toLowerCase());            if (fileType != null && !fileType.equals("")) {                //判断桶是否存在                String bucketName2 = createBucket(fileType.toLowerCase());                if (bucketName2 != null && !bucketName2.equals("")) {                    return bucketName2;                }else{                    return  fileType;                }            }        } catch (Exception e) {            log.error("Error reading bucket name ");        }        return fileType;    }    /**     * 读取配置文件     *     * @param fileType     * @return     * @throws IOException     */    private String getProperty(String fileType) throws IOException {        Properties SysLocalPropObject = new Properties();        //判断桶关系配置文件是否为空        if (SysLocalPropObject.isEmpty()) {            InputStream is = getClass().getResourceAsStream("/BucketRelation.properties");            SysLocalPropObject.load(is);            is.close();        }        return SysLocalPropObject.getProperty("bucket." + fileType);    }    /**     * 文件上传     *     * @param file 文件     * @return Boolean     */    public String upload(MultipartFile file, String bucketName) {        String originalFilename = file.getOriginalFilename();        if (StringUtils.isBlank(originalFilename)) {            throw new RuntimeException();        }        String objectName = file.getName();        try {            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(objectName)                    .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();            //文件名称相同会覆盖            customMinioClient.putObject(objectArgs);        } catch (Exception e) {            e.printStackTrace();            return null;        }        // 查看文件地址        GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(bucketName).object(objectName).method(Method.GET).build();        String url = null;        try {            url = customMinioClient.getPresignedObjectUrl(build);        } catch (ErrorResponseException e) {            e.printStackTrace();        } catch (InsufficientDataException e) {            e.printStackTrace();        } catch (InternalException e) {            e.printStackTrace();        } catch (InvalidKeyException e) {            e.printStackTrace();        } catch (InvalidResponseException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } catch (NoSuchAlgorithmException e) {            e.printStackTrace();        } catch (XmlParserException e) {            e.printStackTrace();        } catch (ServerException e) {            e.printStackTrace();        }        return url;    }//    /**//     * 写入配置文件//     *///    public void setProperty(String bucketName) {//        String tempPath = Objects.requireNonNull(getClass().getResource("/BucketRelation.properties")).getPath();//        OutputStream os;//        try {//            os = new FileOutputStream(tempPath);//            SysLocalPropObject.setProperty(bucketName, bucketName);//            SysLocalPropObject.store(os, "Update " + bucketName + " " + bucketName);//            os.close();//        } catch (IOException e) {//        }//    }}//    @Autowired//    private MinioProp minioProp;//////    @Autowired//    private MinioClient minioClient;////////    /**//     * 列出所有的桶//     *///    public List<String> listBuckets() throws Exception {//        List<Bucket> list = minioClient.listBuckets();//        List<String> names = new ArrayList<>();//        list.forEach(b -> {//            names.add(b.name());//        });//        return names;//    }////    /**//     * 列出一个桶中的所有文件和目录//     *///    public List<Fileinfo> listFiles(String bucket) throws Exception {//        Iterable<Result<Item>> results = minioClient.listObjects(//                ListObjectsArgs.builder().bucket(bucket).recursive(true).build());////        List<Fileinfo> infos = new ArrayList<>();//        results.forEach(r->{//            Fileinfo info = new Fileinfo();//            try {//                Item item = r.get();//                info.setFilename(item.objectName());//                info.setDirectory(item.isDir());//                infos.add(info);//            } catch (Exception e) {//                e.printStackTrace();//            }//        });//        return infos;//    }////    /**//     * 下载一个文件//     *///    public InputStream download(String bucket, String objectName) throws Exception {//        InputStream stream = minioClient.getObject(//                GetObjectArgs.builder().bucket(bucket).object(objectName).build());//        return stream;//    }////    /**//     * 删除一个桶//     *///    public void deleteBucket(String bucket) throws Exception {//        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucket).build());//    }////    /**//     * 删除一个对象//     *///    public void deleteObject(String bucket, String objectName) throws Exception {//        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectName).build());//    }//////    /**//     * 创建一个桶//     *///    public void createBucket(String bucketName) {//        BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucketName).build();//        MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();//        try {//            if (minioClient.bucketExists(bucketExistsArgs))//                return;//            minioClient.makeBucket(makeBucketArgs);//        } catch (Exception e) {//            log.error("创建桶失败:{}", e.getMessage());//            throw new RuntimeException(e);//        }//    }////    /**//     * 上传一个文件//     * @param file       文件//     * @param bucketName 存储桶//     * @return//     *///    public JSONObject uploadFile(MultipartFile file, String bucketName) throws Exception {//        JSONObject res = new JSONObject();//        res.put("code", 0);//        // 判断上传文件是否为空//        if (null == file || 0 == file.getSize()) {//            res.put("msg", "上传文件不能为空");//            return res;//        }//        // 判断存储桶是否存在//        createBucket(bucketName);//        // 文件名//        String originalFilename = file.getOriginalFilename();//        // 新的文件名 = 存储桶名称_时间戳.后缀名//        String fileName = bucketName + "_" + System.currentTimeMillis() +                              originalFilename.substring(originalFilename.lastIndexOf("."));//        // 开始上传//        InputStream inputStream = file.getInputStream();//        PutObjectArgs args = PutObjectArgs.builder().bucket(bucketName).object(fileName)//                .stream(inputStream,inputStream.available(),-1).build();//        minioClient.putObject(args);//        res.put("code", 1);//        res.put("msg", minioProp.getEndpoint() + "/" + bucketName + "/" + fileName);//        return res;//    }

5.CustomMinioClient

package com.xy.config;import com.google.common.collect.Multimap;import io.minio.CreateMultipartUploadResponse;import io.minio.ListPartsResponse;import io.minio.MinioClient;import io.minio.ObjectWriteResponse;import io.minio.errors.*;import io.minio.messages.Part;import java.io.IOException;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;public class CustomMinioClient extends MinioClient {    /**     * 继承父类     * @param client     */    public CustomMinioClient(MinioClient client) {        super(client);    }    /**     * 初始化分片上传、获取 uploadId     *     * @param bucket           String  存储桶名称     * @param region           String     * @param object           String   文件名称     * @param headers          Multimap<String, String> 请求头     * @param extraQueryParams Multimap<String, String>     * @return String     */    public String initMultiPartUpload(String bucket, String region, String object, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, ServerException, InternalException, XmlParserException, InvalidResponseException, ErrorResponseException {        CreateMultipartUploadResponse response = this.createMultipartUpload(bucket, region, object, headers, extraQueryParams);        return response.result().uploadId();    }    /**     * 合并分片     *     * @param bucketName       String   桶名称     * @param region           String     * @param objectName       String   文件名称     * @param uploadId         String   上传的 uploadId     * @param parts            Part[]   分片集合     * @param extraHeaders     Multimap<String, String>     * @param extraQueryParams Multimap<String, String>     * @return ObjectWriteResponse     */    public ObjectWriteResponse mergeMultipartUpload(String bucketName, String region, String objectName, String uploadId, Part[] parts, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, ServerException, InternalException, XmlParserException, InvalidResponseException, ErrorResponseException {        return this.completeMultipartUpload(bucketName, region, objectName, uploadId, parts, extraHeaders, extraQueryParams);    }    /**     * 查询当前上传后的分片信息     *     * @param bucketName       String   桶名称     * @param region           String     * @param objectName       String   文件名称     * @param maxParts         Integer  分片数量     * @param partNumberMarker Integer  分片起始值     * @param uploadId         String   上传的 uploadId     * @param extraHeaders     Multimap<String, String>     * @param extraQueryParams Multimap<String, String>     * @return ListPartsResponse     */    public ListPartsResponse listMultipart(String bucketName, String region, String objectName, Integer maxParts, Integer partNumberMarker, String uploadId, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {        return this.listParts(bucketName, region, objectName, maxParts, partNumberMarker, uploadId, extraHeaders, extraQueryParams);    }}

6.CorsConfig

package com.xy.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.cors.CorsConfiguration;import org.springframework.web.cors.UrlBasedCorsConfigurationSource;import org.springframework.web.filter.CorsFilter;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/** * 全局跨域处理 * @author CV */@Configurationpublic class CorsConfig implements WebMvcConfigurer {        private CorsConfiguration buildConfig() {            CorsConfiguration corsConfiguration = new CorsConfiguration();            corsConfiguration.addAllowedOrigin("*");            corsConfiguration.addAllowedHeader("*");            corsConfiguration.addAllowedMethod("*");            corsConfiguration.setMaxAge(3600L);            corsConfiguration.setAllowCredentials(true);            return corsConfiguration;        }        @Bean        public CorsFilter corsFilter() {            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();            source.registerCorsConfiguration("/**", buildConfig());            return new CorsFilter(source);        }    }

接下来是返回信息工具类

7.ResponseResult

package com.xy.util;import lombok.Data;@Datapublic class ResponseResult<T> {    private int code;    private String enMessage;    private String zhMessage;    private T data;    public ResponseResult() {    }    public ResponseResult(int code, String enMessage, String zhMessage) {        this.code = code;        this.enMessage = enMessage;        this.zhMessage = zhMessage;    }    /**     * 成功     */    public static <T> ResponseResult<T> success() {        ResponseResult<T> result = new ResponseResult<T>();        result.setCode(ResultCode.SUCCESS.getCode());        result.setEnMessage(ResultCode.SUCCESS.getEnMessage());        result.setZhMessage(ResultCode.SUCCESS.getZhMessage());        return result;    }    /**     * 成功     */    public static <T> ResponseResult<T> success(T data) {        ResponseResult<T> result = new ResponseResult<T>();        result.setCode(ResultCode.SUCCESS.getCode());        result.setEnMessage(ResultCode.SUCCESS.getEnMessage());        result.setZhMessage(ResultCode.SUCCESS.getZhMessage());        result.setData(data);        return result;    }    /**     * 失败     */    public static  <T> ResponseResult <T> error() {        ResponseResult<T> result = new ResponseResult<T>();        result.setCode(ResultCode.FAIL.getCode());        result.setEnMessage(ResultCode.FAIL.getEnMessage());        result.setZhMessage(ResultCode.FAIL.getZhMessage());        return result;    }    /**     * 失败     */    public static <T> ResponseResult<T> error(T data) {        ResponseResult<T> result = new ResponseResult<T>();        result.setCode(ResultCode.FAIL.getCode());        result.setEnMessage(ResultCode.FAIL.getEnMessage());        result.setZhMessage(ResultCode.FAIL.getZhMessage());        result.setData(data);        return result;    }    /**     *     * @param data 数据     * @param <T>     * @return     */    public static <T> ResponseResult<T> uploading(T data) {        ResponseResult<T> result = new ResponseResult<T>();        result.setCode(ResultCode.UPLOADING.getCode());        result.setEnMessage(ResultCode.UPLOADING.getEnMessage());        result.setZhMessage(ResultCode.UPLOADING.getZhMessage());        result.setData(data);        return result;    }    /**     * 成功     */    public static <T> ResponseResult<T> success(int code, String enMessage, String zhMessage) {        return new ResponseResult(code, enMessage, zhMessage);    }    /**     * 失败     */    public static <T> ResponseResult<T> error(int code, String enMessage, String zhMessage) {        return new ResponseResult(code, enMessage, zhMessage);    }    public int getCode() {        return code;    }    public void setCode(int code) {        this.code = code;    }    public String getEnMessage() {        return enMessage;    }    public void setEnMessage(String enMessage) {        this.enMessage = enMessage;    }    public String getZhMessage() {        return zhMessage;    }    public void setZhMessage(String zhMessage) {        this.zhMessage = zhMessage;    }    public T getData() {        return data;    }    public void setData(T data) {        this.data = data;    }//    public static ResponseResult<Void> SUCCESS = new ResponseResult<>(200,"成功");//    public static ResponseResult<Void> INTEVER_ERROR = new ResponseResult<>(500,"服务器错误");//    public static ResponseResult<Void> NOT_FOUND = new ResponseResult<>(404,"未找到");}

8.ResultCode

package com.xy.util;/** * http状态码枚举类 */public enum ResultCode {    SUCCESS(1, "Success", "成功"),    UPLOADING(2, "Uploading", "上传中"),    FAIL(-1, "Err", "失败"),    DATABASE_OPERATION_FAILED(504, "数据库操作失败"),    CONTINUE(100, "Continue", "请继续发送请求的剩余部分"),    SWITCHING_PROTOCOLS(101, "Switching Protocols", "协议切换"),    PROCESSING(102, "Processing", "请求将继续执行"),    CHECKPOINT(103, "Checkpoint", "可以预加载"),    OK(200, "OK", "请求已经成功处理"),    CREATED(201, "Created", "请求已经成功处理,并创建了资源"),    ACCEPTED(202, "Accepted", "请求已经接受,等待执行"),    NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information", "请求已经成功处理,但是信息不是原始的"),    NO_CONTENT(204, "No Content", "请求已经成功处理,没有内容需要返回"),    RESET_CONTENT(205, "Reset Content", "请求已经成功处理,请重置视图"),    PARTIAL_CONTENT(206, "Partial Content", "部分Get请求已经成功处理"),    MULTI_STATUS(207, "Multi-Status", "请求已经成功处理,将返回XML消息体"),    ALREADY_REPORTED(208, "Already Reported", "请求已经成功处理,一个DAV的绑定成员被前一个请求枚举,并且没有被再一次包括"),    IM_USED(226, "IM Used", "请求已经成功处理,将响应一个或者多个实例"),    MULTIPLE_CHOICES(300, "Multiple Choices", "提供可供选择的回馈"),    MOVED_PERMANENTLY(301, "Moved Permanently", "请求的资源已经永久转移"),    FOUND(302, "Found", "请重新发送请求"),    SEE_OTHER(303, "See Other", "请以Get方式请求另一个URI"),    NOT_MODIFIED(304, "Not Modified", "资源未改变"),    USE_PROXY(305, "Use Proxy", "请通过Location域中的代理进行访问"),    TEMPORARY_REDIRECT(307, "Temporary Redirect", "请求的资源临时从不同的URI响应请求"),    RESUME_INCOMPLETE(308, "Resume Incomplete", "请求的资源已经永久转移"),    BAD_REQUEST(400, "Bad Request", "请求错误,请修正请求"),    UNAUTHORIZED(401, "Unauthorized", "没有被授权或者授权已经失效"),    PAYMENT_REQUIRED(402, "Payment Required", "预留状态"),    FORBIDDEN(403, "Forbidden", "请求被理解,但是拒绝执行"),    NOT_FOUND(404, "Not Found", "资源未找到"),    METHOD_NOT_ALLOWED(405, "Method Not Allowed", "请求方法不允许被执行"),    NOT_ACCEPTABLE(406, "Not Acceptable", "请求的资源不满足请求者要求"),    PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required", "请通过代理进行身份验证"),    REQUEST_TIMEOUT(408, "Request Timeout", "请求超时"),    CONFLICT(409, "Conflict", "请求冲突"),    GONE(410, "Gone", "请求的资源不可用"),    LENGTH_REQUIRED(411, "Length Required", "Content-Length未定义"),    PRECONDITION_FAILED(412, "Precondition Failed", "不满足请求的先决条件"),    REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large", "请求发送的实体太大"),    REQUEST_URI_TOO_LONG(414, "Request-URI Too Long", "请求的URI超长"),    UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type", "请求发送的实体类型不受支持"),    REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested range not satisfiable", "Range指定的范围与当前资源可用范围不一致"),    EXPECTATION_FAILED(417, "Expectation Failed", "请求头Expect中指定的预期内容无法被服务器满足"),    UNPROCESSABLE_ENTITY(422, "Unprocessable Entity", "请求格式正确,但是由于含有语义错误,无法响应"),    LOCKED(423, "Locked", "当前资源被锁定"),    FAILED_DEPENDENCY(424, "Failed Dependency", "由于之前的请求发生错误,导致当前请求失败"),    UPGRADE_REQUIRED(426, "Upgrade Required", "客户端需要切换到TLS1.0"),    PRECONDITION_REQUIRED(428, "Precondition Required", "请求需要提供前置条件"),    TOO_MANY_REQUESTS(429, "Too Many Requests", "请求过多"),    REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large", "请求头超大,拒绝请求"),    INTERNAL_SERVER_ERROR(500, "Internal Server Error", "服务器内部错误"),    NOT_IMPLEMENTED(501, "Not Implemented", "服务器不支持当前请求的部分功能"),    BAD_GATEWAY(502, "Bad Gateway", "响应无效"),    SERVICE_UNAVAILABLE(503, "Service Unavailable", "服务器维护或者过载,拒绝服务"),    GATEWAY_TIMEOUT(504, "Gateway Timeout", "上游服务器超时"),    HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version not supported", "不支持的HTTP版本"),    VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates", "服务器内部配置错误"),    INSUFFICIENT_STORAGE(507, "Insufficient Storage", "服务器无法完成存储请求所需的内容"),    LOOP_DETECTED(508, "Loop Detected", "服务器处理请求时发现死循环"),    BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded", "服务器达到带宽限制"),    NOT_EXTENDED(510, "Not Extended", "获取资源所需的策略没有被满足"),    NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required", "需要进行网络授权"),    ACCESS_PARAMETER_INVALID(1001,"Invalid access parameter","访问参数无效"),    UPLOAD_FILE_FAILED(1002,"File upload failure","文件上传失败"),    DATA_NOT_EXISTS(1003,"Data does not exist","数据不存在"),    ;    private int code;    private String enMessage;    private String zhMessage;    ResultCode(int code, String enMessage, String zhMessage) {        this.code = code;        this.enMessage = enMessage;        this.zhMessage = zhMessage;    }    ResultCode(int code, String message) {    }    public int getCode() {        return code;    }    public void setCode(int code) {        this.code = code;    }    public String getEnMessage() {        return enMessage;    }    public void setEnMessage(String enMessage) {        this.enMessage = enMessage;    }    public String getZhMessage() {        return zhMessage;    }    public void setZhMessage(String zhMessage) {        this.zhMessage = zhMessage;    }}

9.FileUploadInfo,还有最重要的实体类

package com.xy.entity;import lombok.Data;import lombok.experimental.Accessors;@Data@Accessors(chain = true)public class FileUploadInfo {    //@NotBlank(message = "文件名不能为空")    private String fileName;    // @NotNull(message = "文件大小不能为空")    private Double fileSize;    // @NotBlank(message = "Content-Type不能为空")    private String contentType;    //  @NotNull(message = "分片数量不能为空")    private Integer partCount;    // @NotBlank(message = "uploadId 不能为空")    private String uploadId;    // 桶名称    //private String bucketName;    //md5    private String fileMd5;    //文件类型    private String fileType;    public FileUploadInfo() {    }}

10.RedisRepo

package com.xy.util;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.BoundValueOperations;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Componentpublic class RedisRepo {    @Autowired    private StringRedisTemplate redisTemplate;    public String get(String key) {        BoundValueOperations<String, String> ops = redisTemplate.boundValueOps(key);        return ops.get();    }    public void save(String key,String str){        BoundValueOperations<String, String> ops = redisTemplate.boundValueOps(key);        ops.set(str);    }    public void saveTimeout(String key, String value, long timeout, TimeUnit unit ){        redisTemplate.boundValueOps(key).setIfAbsent(value,timeout,unit);    }    public void delete(String key){        redisTemplate.delete(key);    }    public long expire(String key){        return redisTemplate.opsForValue().getOperations().getExpire(key);    }}

11.yaml配置

minio:  endpoint: http://localhost:9000  accesskey: minioadmin  secretkey: minioadminspring:  redis:    host: localhost    port: 6379

12.pom配置

      <dependency>            <groupId>io.minio</groupId>            <artifactId>minio</artifactId>            <version>8.3.1</version>        </dependency>        <dependency>            <groupId>com.squareup.okhttp3</groupId>            <artifactId>okhttp</artifactId>            <version>4.9.2</version>        </dependency>

本文仅介绍上传流程的简单实现,很多功能未完善,如文件夹上传、上传暂停、停止等功能。代码有何异常或者不完整欢迎在评论区留言


点击全文阅读


本文链接:http://zhangshiyu.com/post/60611.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1