web上传超大文件(最大测试过6G)

HTML页面

这里只是显示页面大概内容,就不把CSS贴出来了

html 复制代码
<div class="file-manager">
  <div class="hr-line-dashed"></div>
             <label title="上传图片" for="inputFile" class="btn btn-primary">
                 <input type="file" accept=".mp3,.mp4,.rar,.7z,.iso,.zip" name="file" id="inputFile" class="hide"> 选择文件
             </label>
             <div>
                 <input type="text" disabled="" id="fileText"  class="form-control">
             </div>
             <div>
                 保存路径(不填为默认路径):<input type="text" name="saveFilePath" placeholder="C:/Download(注意反斜杠)" value="E:/Download" id="saveFilePath" class="form-control">
             </div>

   <button onclick="upload()" class="btn btn-primary btn-block">上传文件(直接合并方式)</button>
             <div>
                 <span>上传进度</span>
                 <small class="pull-right" id="upFilePer">未上传文件 </small>
             </div>
             <div class="progress progress-small">
                 <div style="width: 0%;" class="progress-bar" id="upFilePerImg"></div>
             </div>
             <div class="hr-line-dashed"></div>
         </div>
<script src="js/jquery.min.js?v=2.1.4"></script>
<!-- 用了 layer 弹出层 -->
<script src="js/plugins/layer/layer.min.js"></script>
<script src="js/request.js"></script>

效果图

JS

javascript 复制代码
    $(document).ready(function () {
   		//选择文件触发
        $('input[type="file"]').change(function() {
            var fileName = $(this).prop('files')[0].name;
            $("#upFilePer").text("0%");
            $("#upFilePerImg").attr("style", "width: 0%");
            $("#fileText").val(fileName);
        });
    });
	//上传 这里可以进行多文件上传 已经做了处理 在上方选择多个文件就可以了
    async function upload(){
        var fileList = $("#inputFile")[0].files;
        if(fileList && fileList.length > 0) {
            // 单个切片文件大小为5MB
            const chunkSize = 5242880;
            // 需要分片上传的文件的最小值 10M
            const chunkMinSize = 10485760;
            let chunkFlag = false;
            for(let i = 0; i < fileList.length; i++) {
                if(fileList[i].size > chunkMinSize) {
                    chunkFlag = true;
                }
                // 获取文件的总分片数
                const chunkNum = Math.ceil(fileList[i].size / chunkSize)
                // 取两个md5值作为整体文件的唯一标识
                let fileMd5 = ''
                if (chunkNum >= 2) {
                    let startMd5 = await getMD5(fileList[i].slice(0, 1 * chunkSize))
                    let endMd5 = await getMD5(fileList[i].slice((chunkNum-1) * chunkSize, chunkNum * chunkSize))
                    fileMd5 = startMd5 + endMd5;
                } else {
                    fileMd5 = await getMD5(fileList[i])
                }
                // 判断文件是否存在
                const res = await checkFileExist(fileMd5);
                const { code, data, msg } = res;
                if(code === 21000) {
                    layer.msg('文件秒传成功')
                    console.log('文件在服务器已存在,文件上传成功(大文件秒传原理就是不传)')
                    // 跳过这个文件,不传了
                    continue;
                } else {
                    // 文件不存在,准备上传
                    // 需要分片上传
                    if(chunkFlag) {
                        var loadIndex = layer.load("文件上传中。。。");
                        let start = new Date()
                        for(let currentChunk = 0; currentChunk < chunkNum; currentChunk++) {
                            let formData = new FormData();
                            formData.append("saveFilePath", $("#saveFilePath").val());
                            // 分片上传
                            formData.append("chunkFlag", chunkFlag);
                            // 分片总数
                            formData.append("chunks", chunkNum);
                            // 当前分片数
                            formData.append("currentChunk", currentChunk);
                            // 分片大小
                            formData.append("chunkSize", chunkSize);
                            // 文件类型
                            formData.append('type', fileList[i].type)
                            // 文件总大小
                            formData.append("size", fileList[i].size);
                            // 文件名
                            formData.append("name", fileList[i].name);
                            // 整个文件的id值,及md5值
                            formData.append("fileMd5", fileMd5);
                            // 计算当前文件分片的md5值
                            let currentChunkMd5 = await getMD5(fileList[i].slice(currentChunk * chunkSize, (currentChunk + 1) * chunkSize));
                            formData.append("currentChunkMd5", currentChunkMd5);
                            formData.append("file", fileList[i].slice(currentChunk * chunkSize, (currentChunk + 1) * chunkSize));
                            //await uploadFile(formData) 因发送太快了服务器无法响应 发送完毕后再发下一个
                            //在调试时候本机完全可以响应,但是放到服务器上后,有延时导致会失败
                            let res = await uploadFile(formData);
                            if(res && res.code == 20000){
                                var cc = parseFloat(currentChunk) + 1;
                                var cn = parseFloat(chunkNum);
                                var num = (cc/cn) * 100;
                                num = parseFloat(num).toFixed(2);
                                $("#upFilePer").text(num + "%");
                                $("#upFilePerImg").attr("style", "width: " + num + "%");
                            }else{
                                success = false;
                                layer.msg(res.message);
                                break;
                            }
                        }
                        let end = new Date()
                        layer.close(loadIndex);
                        setTimeout(() => {
                            layer.msg(fileList[i].name + "上传完成,耗时:" + (end - start));
                            //重选获取所有的文件 或者刷新页面 
                            //getFiles();
                        },200)
                    } else {
                        // 直接传,不分片
                        let formData = new FormData();
                        formData.append("saveFilePath", $("#saveFilePath").val());
                        // 分片上传
                        formData.append("chunkFlag", chunkFlag);
                        // 文件类型
                        formData.append('type', fileList[i].type)
                        // 文件总大小
                        formData.append("size", fileList[i].size);
                        // 文件名
                        formData.append("name", fileList[i].name);
                        // 整个文件的id值,及md5值
                        formData.append("fileMd5", fileMd5);
                        // 计算当前文件分片的md5值
                        formData.append("file", fileList[i]);
                        let res = await uploadFile(formData);
                        if(res && res.code == 20000){
                            $("#upFilePer").text("100%");
                            $("#upFilePerImg").attr("style", "width: 100%");
                            layer.msg(fileList[i].name + "上传完成!" );
                            //重选获取所有的文件 或者刷新页面 
                            //getFiles();
                        }else{
                            layer.msg(res.message);
                        }
                    }
                }
            }
        } else {
            console.log('请先选择文件')
            layer.msg('请先选择文件');
        }
    }

    // 计算MD5
    function getMD5(file, fileListID){
        return new Promise((resove, reject) => {
            // 使用sparkMD5的ArrayBuffer类,读取二进制文件
            const spark = new SparkMD5.ArrayBuffer()
            const fileReader = new FileReader()
            // 异步操作,读完后的结果
            fileReader.onload = (e) => {
                // 把文件开始传入spark
                spark.append(e.target.result)
                // spark计算出MD5后的结果
                const _md5 = spark.end()
                resove(_md5)
                // 下面可以写一些自己需要的业务代码, 例如 fileItem.fileMD5 = _md5
            }
            // fileReader读取二进制文件
            fileReader.readAsArrayBuffer(file)
        })
    }
	
	//后台检查文件是否已经上传过了
    async function checkFileExist(fileMd5Id){
        return await getUrlAsync("admin/checkFileExist", {fileMd5Id: fileMd5Id}) ;
    }
    
//下面两个函数 应该在抽取出来的request.js文件中,这里为了方便放到这里 
function getUrlAsync(strUrl, dataParams) {
    return new Promise((resove, reject) => {
        $.ajax({
            url: strUrl,
            data: dataParams,
            type: "GET",
            dataType: 'json',
            success: function (result) {
                if (result.code === 20000) {
                    return resove(result);
                } else if (result.code === 20002) {
                    layer.msg(result.message);
                    setTimeout(function () {
                        window.top.location.href = "index.html";
                    }, 1500);
                } else {
                    layer.alert(result.message);
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                layer.msg("连接超时:" + XMLHttpRequest.status + ","
                    + XMLHttpRequest.readyState + "," + textStatus);
            },
            complete: function (XMLHttpRequest, textStatus) {
                this; // 调用本次AJAX请求时传递的options参数
            }
        });
    })
}

function uploadFile(formdata) {
    return new Promise((resove, reject) => {
        $.ajax({
            url:"admin/uploadFile",
            type:'post',
            contentType:false,
            processData:false,
            data:formdata,
            success: function (result) {
                if (result.code === 20000) {
                    resove(result);
                } else if (result.code === 20002) {
                    layer.msg(result.message);
                    setTimeout(function () {
                        window.top.location.href = "index.html";
                    }, 500);
                } else {
                    reject(result);
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                layer.msg("连接超时:" + XMLHttpRequest.status + ","
                    + XMLHttpRequest.readyState + "," + textStatus);
                reject();
            }
        })
    })
}

JAVA后台

adminCotroller

java 复制代码
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/admin")
public class AdminCotroller {
	private static final Logger logger = LoggerFactory.getLogger(AdminCotroller.class);

    @Autowired
    private FileUploadService fileUploadService;

	@PostMapping("/uploadFile")
    @ResponseBody
    public R uploadFile(FileUploadDTO dto){
        if (ObjectUtils.isEmpty(dto)) {
            return R.error("参数错误,文件上传失败");
        }
        if (dto.getChunkFlag()) {
            return fileUploadService.chunkFileUpload(dto);
        } else {
            return fileUploadService.singleFileUpload(dto);
        }
    }

}

FileUploadDTO.java

java 复制代码
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

/**
 * description:
 *
 * @author ZhouYi
 * @version 1.0.0
 * @ClassName FileUploadDTO
 * @date 2023/10/04 10:26:37
 */
@Data
public class FileUploadDTO {
    /**
     * 是否是分片上传
     */
    @NotNull(message = "是否分片不能为空")
    private Boolean chunkFlag;

    /**
     * 文件
     */
    @NotNull(message = "文件不能为空")
    private MultipartFile file;

    /**
     * 文件名
     */
    @NotBlank(message = "文件名不能为空")
    private String name;

    /**
     * 文件总大小
     */
    private Long size;

    /**
     * 文件md5
     */
    @NotBlank(message = "文件md5不能为空")
    private String fileMd5;

    /**
     * 文件类型
     */
    private String type;

    /**
     * 当前分片
     */
    private Integer currentChunk;

    /**
     * 分片长度
     */
    private Long chunkSize;

    /**
     * 总分片数量
     */
    private Integer chunks;

    /**
     * 分片文件md5
     */
    private String currentChunkMd5;

    /**
     * 文件保存路径
     */
    private String saveFilePath;
}

R.java

java 复制代码
import java.io.Serializable;

/**
 * description:
 *
 * @author ZhouYi
 * @version 1.0.0
 * @ClassName R
 * @date 2022/04/06 21:38:12
 */
public class R implements Serializable {

    private static final long serialVersionUID = -4301232631736358183L;
    /**
     * 请求成功或失败
     */
    private boolean status;

    private int code;

    private Object data;

    private String message;

    /**
     * 返回成功无需数据 ok
     *
     * @return
     */
    public static R success() {
        return new R(ServerConstant.IS_SUCCESS, ServerConstant.MESSAGE_SUCCESS_CODE, ServerConstant.MESSAGE_SUCCESS);
    }

    /**
     * 提交成功有提示 操作成功
     * @return
     */
    public static R successMsg() {
        return new R(ServerConstant.IS_SUCCESS, ServerConstant.MESSAGE_SUCCESS_CODE, ServerConstant.ERROR_SAVE_SUCCESS);
    }

    /**
     * 返回成功 data
     * @param data 自定义数据
     * @return
     */
    public static R success(Object data) {
        return new R(ServerConstant.IS_SUCCESS, ServerConstant.MESSAGE_SUCCESS_CODE, data, ServerConstant.MESSAGE_SUCCESS);
    }
    /**
     * 返回成功 message
     * @param message 自定义数据
     * @return
     */
    public static R success(String  message) {
        return new R(ServerConstant.IS_SUCCESS, ServerConstant.MESSAGE_SUCCESS_CODE,message);
    }
    /**
     * 自定义IS_SUCCESS  错误代码 错误消息
     *
     * @param code         错误代码
     * @param errorMessage 错误消息
     * @return
     */
    public static R error(int code, String errorMessage) {
        return new R(ServerConstant.IS_SUCCESS, code, errorMessage);
    }

    /**
     * 保存失败 无需数据
     *
     * @return
     */
    public static R error() {
        return new R(ServerConstant.IS_SUCCESS, ServerConstant.MESSAGE_ERROR_CODE, ServerConstant.ERROR_SAVE_FILE);
    }

    /**
     * 错误 IS_SUCCESS message
     *
     * @param message message
     * @return
     */
    public static R error(String message) {
        return new R(ServerConstant.IS_SUCCESS, ServerConstant.MESSAGE_ERROR_CODE, message);
    }

    /**
     * 服务器错误 请重试 IS_ERROR
     *
     * @return
     */
    public static R errorException() {
        return new R(ServerConstant.IS_ERROR, ServerConstant.MESSAGE_ERROR_CODE, ServerConstant.ERROR_EXCEPTION);
    }

    /**
     * 服务器错误 错误内容自定义 IS_ERROR
     *
     * @param message 自定义内容
     * @return R
     */
    public static R errorException(String message) {
        return new R(ServerConstant.IS_ERROR, message);
    }

    /**
     * 数据格式错误
     *
     * @return
     */
    public static R errorDataFormat() {
        return new R(ServerConstant.IS_SUCCESS, ServerConstant.MESSAGE_ERROR_CODE, ServerConstant.ERROR_DATA_FORMAT);
    }

    /**
     * 请求太频繁
     *
     * @return
     */
    public static R errorRequestsTooFrequently() {
        return new R(ServerConstant.IS_SUCCESS, ServerConstant.MESSAGE_ERROR_CODE, ServerConstant.ERROR_REQUESTS_TOO_FREQUENTLY);
    }

    public R() {

    }

    /**
     * 全部
     *
     * @param status
     * @param code
     * @param data
     * @param message
     */
    public R(boolean status, int code, Object data, String message) {
        this.status = status;
        this.code = code;
        this.data = data;
        this.message = message;
    }

    /**
     * 返回  status data code
     *
     * @param code status
     * @param message message
     * @param code code
     */
    public R(boolean status, int code, String message) {
        this.status = status;
        this.code = code;
        this.message = message;
    }

    /**
     * 只需返回  status message
     *
     * @param status  status
     * @param message message
     */
    public R(boolean status, String message) {
        this.status = status;
        this.code = ServerConstant.MESSAGE_SUCCESS_CODE;
        this.message = message;
    }

    public boolean isStatus() {
        return status;
    }

    public void setStatus(boolean status) {
        this.status = status;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return "R{" +
                "code=" + code +
                ", data=" + data +
                ", message='" + message + '\'' +
                '}';
    }
}

ServerConstant.java

java 复制代码
import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;

public interface ServerConstant {
 /**
     * 请求成功
     */
    public static final boolean IS_SUCCESS = true;
    /**
     * 请求失败 一般为服务器抛异常
     */
    public static final boolean IS_ERROR = false;
    /**
     * 前台显示
     */
    public static final Integer IS_SHOW = 1;
    /**
     * 前台不显示
     */
    public static final Integer IS_NOT_SHOW = 0;
    /**
     * 未删除
     */
    public static final Integer IS_NOT_DELETE = 0;
    /**
     * 删除
     */
    public static final Integer IS_DELETE = 1;
    /**
     * 每页数量
     */
    public static final Integer SIZE = 35;
    /**
     * 当前页
     */
    public static final Integer CURRENT = 1;
    /**
     * 默认
     */
    public static final long COMPARE = 10 * 60 * 60 * 60;

    /**
     * 请求成功 20000  ok
     */
    public static final int MESSAGE_SUCCESS_CODE = 20000;

    public static final String MESSAGE_SUCCESS = "ok";
    /**
     * 请求失败 20001  error
     */
    public static final int MESSAGE_ERROR_CODE = 20001;

    /**
     * 登录失效 20002  error
     */
    public static final int MESSAGE_LOG_FILE_CODE = 20002;
    /**
     * 文件存在 21000
     */
    public static final int FILE_EXSIT = 21000;
    /**
     * 文件正在获取hash 220000
     */
    public static final int FILE_GET_HASH = 220000;

    public static final String FILE_GET_HASH_ING = "FILE_GET_HASH_ING";

    public static final String FILE_GET_HASH_FINISH = "FILE_GET_HASH_FINISH";
    /**
     * 20010 操作成功
     */
    public static final String ERROR_SAVE_SUCCESS = "操作成功!";
    /**
     * 20011 操作失败
     */
    public static final String ERROR_SAVE_FILE = "操作失败!";
    /**
     * 30001 数据格式错误
     */
    public static final String ERROR_DATA_FORMAT = "数据格式错误!";

    /**
     * 30002 请稍后再试
     */
    public static final String ERROR_REQUESTS_TOO_FREQUENTLY = "请稍后再试!";

    /**
     * 服务器错误 30003 请重试
     */
    public static final String ERROR_EXCEPTION = "请重试!";

    public static final String COMMUNICATION_TOKEN = "communicationToken";

    public static final String OPEN = "open";

    public static final String CLOSE = "close";

    public static final int FILE_MD5_CHECK_EXCEPTION = 23000;

    public static final String MD5_CHECK_EXCEPTION = "MD5校验异常";

    public static final String FILE_EXSIT_MSG = "文件已存在!";


    /**
     * 上传完成
     */
    public static final int FILE_UPLOAD_COMPLETED = 21001;

    /**
     * 上传中
     */
    public static final int FILE_UPLOAD_UPLOADING = 21002;

    /**
     * 上传失败
     */
    public static final int FILE_UPLOAD_FAILED = 21003;

    /**
     * 文件未查询到
     */
    public static final int FILE_NOT_FIND = 21004;

    public static final String WX_ACCESS_TOKEN = "WX_ACCESS_TOKEN";

    /**
     * description 保存文件上传状态
     */
    public static Cache<String, FILE_STATE> fileStateCahe = CacheUtil.newFIFOCache(100);

}

FileUploadService.java

java 复制代码
package com.xyhz.backup.server.service;

import com.xyhz.backup.server.entity.VO.FileUploadDTO;
import com.xyhz.backup.server.entity.VO.R;

/**
 * @ClassName FileUploadService
 */
public interface FileUploadService {
    /**
     * 分片上传
     * @param dto
     */
    R chunkFileUpload(FileUploadDTO dto);

    /**
     * 单文件上传
     * @param dto
     */
    R singleFileUpload(FileUploadDTO dto);

}

FileUploadServiceImpl.java

java 复制代码
@Slf4j
@Service
public class FileUploadServiceImpl implements FileUploadService {
    @Autowired
    private FileUploadUtil fileUploadUtil;

    @Autowired
    private SysFileMapper sysFileMapper;

    @Autowired
    private SysChunkRecordMapper sysChunkRecordMapper;
    
 	@Override
    public R chunkFileUpload(FileUploadDTO dto) {
        UploadResultVo vo = new UploadResultVo();
        // 检测文件是否存在,大文件秒传
        //SysFile sysFile = queryMd5(dto.getFileMd5());
        //if (null != sysFile) {
        //    vo.setFileKid(sysFile.getKid()).setUploaded(Boolean.TRUE);
        //    return R.success(vo);
        //}
        // 检测分片是否存在
        //SysChunkRecord scr = queryChunkMd5(dto.getCurrentChunkMd5());
        //if (scr != null) {
        //    vo.setFileKid(scr.getKid()).setUploaded(Boolean.TRUE);
        //    return R.success(vo);
        //}
        try {
            MultipartFile multipartFile = dto.getFile();
            String filePath = fileUploadUtil.getFilePath(dto.getSaveFilePath());
            File file = new File(filePath, dto.getName());
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
            if (randomAccessFile.length() == 0L) {
                randomAccessFile.setLength(dto.getSize());
            }
            // 计算分片文件的位置
            long pos = dto.getCurrentChunk() * dto.getChunkSize();
            FileChannel channel = randomAccessFile.getChannel();
            //log.info( "pos: "+ pos + "size: " + multipartFile.getSize());
            MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, pos, multipartFile.getSize());
            map.put(multipartFile.getBytes());
            cleanBuffer(map);
            channel.close();
            randomAccessFile.close();
            // 存分片数据
            String chunkKid = saveSysChunkRecord(file, dto);
            vo.setChunkKid(chunkKid).setUploaded(Boolean.TRUE);
            if (dto.getCurrentChunk() == dto.getChunks() - 1) {
                LambdaQueryWrapper<SysChunkRecord> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(SysChunkRecord::getFileMd5, dto.getFileMd5());
                Integer integer = sysChunkRecordMapper.selectCount(wrapper);
                int flag = 0;
                while (!integer.equals(dto.getChunks()) && flag < 10) {
                    //Thread.sleep(100);
                    integer = sysChunkRecordMapper.selectCount(wrapper);
                    flag++;
                }
                if(integer.equals(dto.getChunks())) {
                    // 存文件
                    SysFile fileInfo = buildSysFile(dto, file);
                    int insert = sysFileMapper.insert(fileInfo);
                    if (insert == 1) {
                        // 清除分片数据
                        cleanChunkData(dto.getFileMd5());
                    }
                    vo.setFileKid(fileInfo.getKid()).setUploaded(Boolean.TRUE);
                } else {
                    // 清除分片数据
                    cleanChunkData(dto.getFileMd5());
                    // 文件上传失败
                    return R.error("分片上传失败!");
                }
            }
            return R.success(vo);
        } catch (Exception e) {
            log.error("分片上传失败!",e);
            return R.error("分片上传失败!");
        }
    }
	 @Override
    public R singleFileUpload(FileUploadDTO dto) {
        try {
            UploadResultVo vo = new UploadResultVo();
            SysFile sysFile = queryMd5(dto.getFileMd5());
            if (null != sysFile) {
                vo.setFileKid(sysFile.getKid()).setUploaded(Boolean.TRUE);
                return R.success(vo);
            }
            // 存文件
            String filePath = fileUploadUtil.getFilePath(dto.getSaveFilePath());
            File file = new File(filePath, dto.getName());
            dto.getFile().transferTo(file);
            // 存数据库
            SysFile fileInfo = buildSysFile(dto, file);
            sysFileMapper.insert(fileInfo);
            vo.setFileKid(fileInfo.getKid()).setUploaded(Boolean.TRUE);
            return R.success(vo);
        } catch (IOException e) {
            log.error("单文件上传失败!",e);
            return R.error("单文件上传失败!");
        }
    }
    
        /**
     * 根据md5值清除分片数据
     */
    private void cleanChunkData(String md5) {
        LambdaQueryWrapper<SysChunkRecord> wrapper = new LambdaQueryWrapper<SysChunkRecord>()
                .eq(SysChunkRecord::getFileMd5, md5);
        sysChunkRecordMapper.delete(wrapper);
    }
 	/**
     * 构建SysFile
     * @param dto
     * @param file
     * @return
     */
    private SysFile buildSysFile(FileUploadDTO dto, File file) {
        return SysFile.builder()
                .fileMd5(dto.getFileMd5())
                .fileName(dto.getName())
                .filePath(file.getAbsolutePath())
                .fileType(dto.getType())
                .fileSize(dto.getSize())
                .createTime(LocalDateTime.now())
                .extension(dto.getName().substring(dto.getName().lastIndexOf(".")))
                .build();
    }
}

SysChunkRecordMapper.java

java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import server.entity.SysChunkRecord;
 public interface SysChunkRecordMapper extends BaseMapper<SysChunkRecord> {
}

SysChunkRecord.java

java 复制代码
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.time.LocalDateTime;
@TableName(value ="sys_chunk_record")
@Data
@Accessors(chain = true)
public class SysChunkRecord implements Serializable {
    /**
     * kid
     */
    @TableId(type = IdType.ASSIGN_UUID)
    private String kid;

    /**
     * 文件名称
     */
    private String chunkFileName;

    /**
     * 文件存放路径
     */
    private String chunkFilePath;

    /**
     * 文件md5
     */
    private String fileMd5;

    /**
     * 文件分片md5
     */
    private String currentChunkMd5;

    /**
     * 分片大小
     */
    private Long chunkSize;

    /**
     * 第几块分片
     */
    private Integer currentChunk;

    /**
     * 总分片数量
     */
    private Integer chunks;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

FileUploadUtil.java

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

@Slf4j
@Component
public class FileUploadUtil {
 /**
     * 生成文件路径
     * @return
     */
    public String getFilePath(String saveFilePath){
        if (StringUtils.isEmpty(saveFilePath)) {
            //没有上传路径就获取默认配置,这里可以直接指定一个
            saveFilePath = "E:/Download"
            //saveFilePath = serverConfig.getDefaultUploadPath();
        }
        File file = new File(saveFilePath);
        if (!file.exists()) {
            file.mkdirs();
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM");
        LocalDate now = LocalDate.now();
        String format = formatter.format(now);
        String datePath = saveFilePath + File.separator + format;
        File fullFile = new File(datePath);
        if (!fullFile.exists()) {
            fullFile.mkdirs();
        }
        return datePath;
    }
}

SysFile.java

java 复制代码
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@TableName(value ="sys_file")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SysFile implements Serializable {
    /**
     * kid
     */
    @TableId(type = IdType.ASSIGN_UUID)
    private String kid;

    /**
     * 文件名称
     */
    private String fileName;

    /**
     * 文件扩展名
     */
    private String extension;

    /**
     * 文件大小
     */
    private Long fileSize;

    /**
     * 文件类型
     */
    private String fileType;

    /**
     * 文件存放路径
     */
    private String filePath;

    /**
     * 文件md5
     */
    private String fileMd5;

    /**
     * 备注
     */
    private String remark;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

Maven

xml 复制代码
	<dependency>
	    <groupId>com.baomidou</groupId>
	    <artifactId>mybatis-plus-core</artifactId>
	    <version>3.4.2</version>
	</dependency>
	<dependency>
	    <groupId>com.baomidou</groupId>
	    <artifactId>mybatis-plus-boot-starter</artifactId>
	    <version>3.4.2</version>
	</dependency>
	     <dependency>
	    <groupId>cn.hutool</groupId>
	    <artifactId>hutool-all</artifactId>
	    <version>5.8.0.M2</version>
	</dependency>
相关推荐
我要学编程(ಥ_ಥ)1 小时前
滑动窗口算法专题(1)
java·数据结构·算法·leetcode
niceffking1 小时前
JVM 一个对象是否已经死亡?
java·jvm·算法
真的很上进1 小时前
【Git必看系列】—— Git巨好用的神器之git stash篇
java·前端·javascript·数据结构·git·react.js
胖虎哥er1 小时前
Html&Css 基础总结(基础好了才是最能打的)三
前端·css·html
qq_278063711 小时前
css scrollbar-width: none 隐藏默认滚动条
开发语言·前端·javascript
.ccl1 小时前
web开发 之 HTML、CSS、JavaScript、以及JavaScript的高级框架Vue(学习版2)
前端·javascript·vue.js
小徐不会写代码1 小时前
vue 实现tab菜单切换
前端·javascript·vue.js
科研小白_d.s1 小时前
intellij-idea创建html项目
java·html·intellij-idea
XXXJessie1 小时前
c++249多态
java·c++·servlet
2301_765347541 小时前
Vue3 Day7-全局组件、指令以及pinia
前端·javascript·vue.js