SpringBoot文件上传

SpringBoot文件上传

文件上传的本质

文件上传的流程,相当于是复制,是读取了要上传文件的二进制流,并把这个二进制流的信息复制给服务器指定路径的另一个文件

文件上传的细节点

  • 在服务器中需要有专门的目录来存放上传路径 ,在新的上传请求传来时,必应判断该上传路径是否存在,若不存在 ,则需要先创建该上传目录。

  • 在服务器中的文件,为了防止上传文件的名称重复(防止重复提交同一文件),从而产生冲突等问题,应用 UUID 来生成随机 ID 为新文件命名

  • 文件上传应当将上传至服务器的url地址保存在数据库中

获取原文件名称

在前端向后端发起上传请求时,传参中必然带有文件信息,此时在 Controller 层对应的接口中会接收到 MultipartFile 类型的 file

通过以下代码可获取原文件的信息:

//原文件名称
String OriginFileName = file.getOriginalFilename();
//原文件文件名最大长度
int fileNamelength = file.getOriginalFilename().length()

获取新文件路径(名称)

/**
     * 编码文件名
     */
    public static final String extractFilename(MultipartFile file)
    {
        String fileName = file.getOriginalFilename();
        //获得文件名后缀
        String extension = getExtension(file);
        //拼接上时间
        fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
        return fileName;
    }

为什么要拼接上时间,是因为window类似mysql也有自己的索引规则,通过时间来查找,能减轻搜索压力

将远程文件存入本地

假设,存放新文件的变量名称为 newFile ,远程文件变量名称为 oldFile

则可通过以下代码将远程文件保存至本地文件中:

oldFile.transferTo(newFile);

修改上传文件类型

upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);

其中DEFAULT_ALLOWED_EXTENSION是上传时候默认携带的一个参数,存储了一个集合(文件类型)
在这里插入图片描述
可以发现这里面没有视频的类型,要想该上传可以通过视频类型的文件,可以在这个集合上加上视频对应的类型
在这里插入图片描述

案例

前端采取ElementUI上传的组件

这个项目上传是需要验证权限的,所以请求时候在请求头携带了一个认证的参数

<template>
<!--  普通单个文件上传-->
    <div  class="component-upload-image">
        <el-upload v-if="chooseFlag ==0"
                drag
                :action="uploadImgUrl"
                :on-success="handleUploadSuccess"
                :before-upload="handleBeforeUpload"
                :on-error="handleUploadError"
                name="file"
                :show-file-list="false"
                :headers="headers"
        >
             <i class="el-icon-upload"></i>
             <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
        </el-upload>

</template>

<script>
    import { getToken } from '@/utils/auth';

    export default {
        name:"Upload",
        components: {},

        data() {
            return {
                headers: {
                    Authorization: "Bearer " + getToken(),
                },
                uploadUrlPath: "没有文件上传",
            };
        },
        props: {
            value: {
                type: String,
                default: "",
            },
            chooseFlag: {
              value: Number,
              default: 0,
            },
            uploadImgUrl: {
              type: String,
              default: "/v1/admin/common/upload", // 上传的图片服务器地址
            },
        },
        methods: {
            handleUploadSuccess(res) {
                this.uploadUrlPath = JSON.stringify(res);
                this.loading.close();
                //把uploadUrlPath封装在upload-event,别的页面可以通过组件upload-event来调用这个属性
                this.$emit("upload-event",this.uploadUrlPath);
            },
            handleBeforeUpload() {
                this.loading = this.$loading({
                    lock: true,
                    text: "上传中",
                    background: "rgba(0, 0, 0, 0.7)",
                });
            },
            handleUploadError() {
                this.$message({
                    type: "error",
                    message: "上传失败",
                });
                this.loading.close();
            },
        },
        watch: {},
    };
</script>

<style scoped lang="scss">
    .avatar {
        width: 100%;
        height: 100%;
    }
</style>

后端

CommonController

这里处理上传后的文件名称和请求路径进行一个拼接

public class CommonController {
    @Autowired
    private ServerConfig serverConfig;
    /**
     * 通用上传请求
     */
    @PostMapping("/upload")
    public ResultJson uploadFile(MultipartFile file) throws Exception {
        try {
            // 上传文件路径(配置文件中的)
            String filePath = NiuaConfig.getUploadPath();
            // 上传并返回新文件名称
            String fileName = FileUploadUtils.upload(filePath, file);
            //获取请求路径
            String url = serverConfig.getUrl() + fileName;//http://127.0.0.1:9527
            Map<String, String> prams = new HashMap<String, String>();
            prams.put("fileName", fileName);
            //profile/upload/2022/07/28/94ac7ece-3371-40fb-80d0-8f42231df2f7.jpg
            prams.put("url", url);
            //http://127.0.0.1:9527/profile/upload/2022/07/28/94ac7ece-3371-40fb-80d0-8f42231df2f7.jpg
            return ResultJson.ok(prams);
        } catch (Exception e) {
            return ResultJson.failure(ResultCode.BAD_REQUEST, e.getMessage());
        }
    }
}

获取新文件名称

String fileName = FileUploadUtils.upload(filePath, file);

根据文件路径上传,这里携带了DEFAULT_ALLOWED_EXTENSION参数,表示默认文件上传的类型

 /**
     * 根据文件路径上传
     *
     * @param baseDir 相对应用的基目录
     * @param file 上传的文件
     * @return 文件名称
     * @throws IOException
     */
    public static final String upload(String baseDir, MultipartFile file) throws IOException
    {
        try
        {
            return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        }
        catch (Exception e)
        {
            throw new IOException(e.getMessage(), e);
        }
    }

文件上传

/**
     * 文件上传
     *
     * @param baseDir 相对应用的基目录
     * @param file 上传的文件
     * @param allowedExtension 上传文件类型
     * @return 返回上传成功的文件名
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws FileNameLengthLimitExceededException 文件名太长
     * @throws IOException 比如读写文件出错时
     * @throws InvalidExtensionException 文件校验异常
     */
    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
            InvalidExtensionException
    {
        //文件大小检验
        int fileNamelength = file.getOriginalFilename().length();
        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
        {
            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
        }
        //文件类型检验
        assertAllowed(file, allowedExtension);
        //获取拼接说时间的文件名称(编码新文件名)
        String fileName = extractFilename(file);
		//返回一个文件的绝对路径
        File desc = getAbsoluteFile(baseDir, fileName);
        //把远程文件存入本地
        file.transferTo(desc);
        String pathFileName = getPathFileName(baseDir, fileName);
        return pathFileName;//profile/upload/2022/07/28/94ac7ece-3371-40fb-80d0-8f42231df2f7.jpg
    }

返回的数据格式ResultJson.java

package tech.niua.common.model;

import lombok.Data;

import java.io.Serializable;

/**
 * @author Wangzhen
 * RESTful API 返回类型
 * createAt: 2020/5/29
 */
@Data
public class ResultJson<T> implements Serializable{

    private static final long serialVersionUID = 783015033603078674L;
    private int code;
    private String msg;
    private T data;

    public static ResultJson ok() {
        return ok("");
    }

    public static ResultJson ok(Object o) {
        return new ResultJson(ResultCode.SUCCESS, o);
    }

    public static ResultJson failure(ResultCode code) {
        return failure(code, "");
    }

    public static ResultJson failure(ResultCode code, Object o) {
        return new ResultJson(code, o);
    }

    public ResultJson(ResultCode resultCode) {
        setResultCode(resultCode);
    }

    public ResultJson(ResultCode resultCode, T data) {
        setResultCode(resultCode);
        this.data = data;
    }

    public void setResultCode(ResultCode resultCode) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
    }

    @Override
    public String toString() {
        return "{" +
                "\"code\":" + code +
                ", \"msg\":\"" + msg + '\"' +
                ", \"data\":\"" + data + '\"'+
                '}';
    }
}

返回的数据格式ResultJson.java

package tech.niua.common.model;

import lombok.Data;

import java.io.Serializable;

/**
 * @author Wangzhen
 * RESTful API 返回类型
 * createAt: 2020/5/29
 */
@Data
public class ResultJson<T> implements Serializable{

    private static final long serialVersionUID = 783015033603078674L;
    private int code;
    private String msg;
    private T data;

    public static ResultJson ok() {
        return ok("");
    }

    public static ResultJson ok(Object o) {
        return new ResultJson(ResultCode.SUCCESS, o);
    }

    public static ResultJson failure(ResultCode code) {
        return failure(code, "");
    }

    public static ResultJson failure(ResultCode code, Object o) {
        return new ResultJson(code, o);
    }

    public ResultJson(ResultCode resultCode) {
        setResultCode(resultCode);
    }

    public ResultJson(ResultCode resultCode, T data) {
        setResultCode(resultCode);
        this.data = data;
    }

    public void setResultCode(ResultCode resultCode) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
    }

    @Override
    public String toString() {
        return "{" +
                "\"code\":" + code +
                ", \"msg\":\"" + msg + '\"' +
                ", \"data\":\"" + data + '\"'+
                '}';
    }
}

返回的数据格式ResultJson.java

package tech.niua.common.model;

import lombok.Data;

import java.io.Serializable;

/**
 * @author Wangzhen
 * RESTful API 返回类型
 * createAt: 2020/5/29
 */
@Data
public class ResultJson<T> implements Serializable{

    private static final long serialVersionUID = 783015033603078674L;
    private int code;
    private String msg;
    private T data;

    public static ResultJson ok() {
        return ok("");
    }

    public static ResultJson ok(Object o) {
        return new ResultJson(ResultCode.SUCCESS, o);
    }

    public static ResultJson failure(ResultCode code) {
        return failure(code, "");
    }

    public static ResultJson failure(ResultCode code, Object o) {
        return new ResultJson(code, o);
    }

    public ResultJson(ResultCode resultCode) {
        setResultCode(resultCode);
    }

    public ResultJson(ResultCode resultCode, T data) {
        setResultCode(resultCode);
        this.data = data;
    }

    public void setResultCode(ResultCode resultCode) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
    }

    @Override
    public String toString() {
        return "{" +
                "\"code\":" + code +
                ", \"msg\":\"" + msg + '\"' +
                ", \"data\":\"" + data + '\"'+
                '}';
    }
}

编码新文件名

通过UUID(避免重复)给文件编码新的文件名,并且按照指定的样式返回

/**
     * 编码文件名
     */
    public static final String extractFilename(MultipartFile file)
    {
        String fileName = file.getOriginalFilename();
        //获得文件名后缀
        String extension = getExtension(file);
        //拼接上时间
        fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
        return fileName;
    }

上传成功效果图
在这里插入图片描述


版权声明:本文为m0_61820867原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>