SpringBoot+El-upload實現上傳文件到通用上傳接口並返回文件全路徑(若依前后端分離版源碼分析)


場景

SpringBoot+ElementUI實現通用文件下載請求(全流程圖文詳細教程):

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/107983093

在使用了通用下載接口下載一些文件后,有些業務比如上傳頭像,富文本上傳照片,此類業務都需要將

某些文件上傳到服務器上,並返回該文件的全路徑,給具體的業務前端返回,進行回顯數據或下載文件用。

同時為了后期文件數量過多,最好是按照時間在服務器上新建不用的文件夾,並且給上傳的文件的

文件名使用當前時間的時間戳進行重命名,避免文件重名。

 

 

 

 

 

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi
關注公眾號
霸道的程序猿
獲取編程相關電子書、教程推送與免費下載。

實現

首先在頁面添加一個el-upload控件

    <el-upload
      class="quill-img"
      :action="uploadImgUrl"
      name="file"
      :headers="headers"
      :show-file-list="false"
      :on-success="quillImgSuccess"
      :on-error="uploadError"
      :before-upload="quillImgBefore"
      accept='.jpg,.jpeg,.png,.gif'
    ></el-upload>

這里添加了一些樣式,具體的樣式代碼

.quill-img {
  display: none;
}

目的是先讓其隱藏,只有在某些操作下才會觸發上傳的選擇框。

那么怎樣觸發這文件上傳組件的點擊事件,可以在需要彈出文件上傳框的業務方法中,比如上傳按鈕的點擊事件中

// 觸發input框選擇圖片文件
document.querySelector(".quill-img input").click();

action對應的是文件上傳的url

需要在屬性中提前聲明

  data() {
    return {
      uploadImgUrl: "",

name是名稱屬性

headers設置請求頭攜帶token

請求頭里攜帶token,請求頭的設置參考如下

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108345222

show-file-list="false"設置不顯示文件列表

再設置它的一些事件

      :on-success="quillImgSuccess"
      :on-error="uploadError"
      :before-upload="quillImgBefore"

對上傳前,上傳成功和失敗的事件進行重寫

    // 圖片上傳前
    quillImgBefore(file) {
      let fileType = file.type;
   if(fileType === 'image/jpeg' || fileType === 'image/png'){
    return true;
   }else {
    this.$message.error('請插入圖片類型文件(jpg/jpeg/png)');
    return false;
   }
    },

    quillImgSuccess(res, file) {
      // res為圖片服務器返回的數據
      // 獲取富文本組件實例
      let quill = this.$refs.quillEditor.quill;
      // 如果上傳成功
      if (res.code == 200) {
        // 獲取光標所在位置
        let length = quill.getSelection().index;
        // 插入圖片  res.url為服務器返回的圖片地址
        quill.insertEmbed(length, "image", res.url);
        // 調整光標到最后
        quill.setSelection(length + 1);
      } else {
        this.$message.error("圖片插入失敗");
      }
    },
    // 富文本圖片上傳失敗
    uploadError() {
      // loading動畫消失
      this.$message.error("圖片插入失敗");
    }

注意這里的圖片上傳成功是事件中,圖片上傳對應的后台SpringBoot接口 

    @PostMapping("/common/upload")
    public AjaxResult uploadFile(MultipartFile file) throws Exception {
        try {
            // 上傳文件路徑
            String filePath = RuoYiConfig.getUploadPath();
            // 上傳並返回新文件名稱
            String fileName = FileUploadUtils.upload(filePath, file);
            String url = serverConfig.getUrl() + fileName;
            AjaxResult ajax = AjaxResult.success();
            ajax.put("fileName", fileName);
            ajax.put("url", url);
            return ajax;
        } catch (Exception e) {
            return AjaxResult.error(e.getMessage());
        }
    }

在此接口中

String filePath = RuoYiConfig.getUploadPath();

是在application.yml中獲取配置的文件上傳的路徑

 

 

 

在application.yml中配置文件上傳的路徑

ruoyi:
  # 名稱
  name: RuoYi
  # 版本
  version: 2.3.0
  # 版權年份
  copyrightYear: 2019
  # 實例演示開關
  demoEnabled: true
  # 文件路徑 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: D:/ruoyi/uploadPath

 然后就是使用配置類來獲取這個配置的profile屬性。

首先在SpringBoot項目目錄下新建config目錄,然后新建配置類RuoYiConfig,名字隨意

然后在配置類上添加注解

@Component
@ConfigurationProperties(prefix = "ruoyi")
public class RuoYiConfig

 

注意這里的prefix屬性值與上面配置文件的根元素一致

然后配置類中的屬性與配置文件根節點下的名稱一致 ,配置類完整代碼

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 讀取項目相關配置
 *
 */
@Component
@ConfigurationProperties(prefix = "ruoyi")
public class RuoYiConfig
{
    /** 項目名稱 */
    private String name;

    /** 版本 */
    private String version;

    /** 版權年份 */
    private String copyrightYear;

    /** 實例演示開關 */
    private boolean demoEnabled;

    /** 上傳路徑 */
    private static String profile;


    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public String getVersion()
    {
        return version;
    }

    public void setVersion(String version)
    {
        this.version = version;
    }

    public String getCopyrightYear()
    {
        return copyrightYear;
    }

    public void setCopyrightYear(String copyrightYear)
    {
        this.copyrightYear = copyrightYear;
    }

    public boolean isDemoEnabled()
    {
        return demoEnabled;
    }

    public void setDemoEnabled(boolean demoEnabled)
    {
        this.demoEnabled = demoEnabled;
    }

    public static String getProfile()
    {
        return profile;
    }

    public void setProfile(String profile)
    {
        RuoYiConfig.profile = profile;
    }

    /**
     * 獲取上傳路徑
     */
    public static String getUploadPath()
    {
        return getProfile() + "/upload";
    }
}

 

這里的配置類的

private static String profile;

就能獲取到application.yml中配置的profile的屬性值了。

為了或此屬性值更加便捷,又新增了一個靜態方法

    public static String getUploadPath()
    {
        return getProfile() + "/upload";
    }

這樣就能通過類直接調用方法。

然后還拼接了一層目錄。這樣通過

RuoYiConfig.getUploadPath();

獲取的路徑就是

D:/ruoyi/uploadPath/upload

在獲取了文件上傳的路徑之后調用了文件上傳工具類的upload方法

    /**
     * 根據文件路徑上傳
     *
     * @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);
        }
    }

此方法又追加了一個參數MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION

此參數作用為限制文件上傳的類型。

    public static final String[] DEFAULT_ALLOWED_EXTENSION = {
            // 圖片
            "bmp", "gif", "jpg", "jpeg", "png",
            // word excel powerpoint
            "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
            // 壓縮文件
            "rar", "zip", "gz", "bz2",
            // pdf
            "pdf" };

然后在調用的方法

upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);

    /**
     * 文件上傳
     *
     * @param baseDir 相對應用的基目錄
     * @param file 上傳的文件
     * @param extension 上傳文件類型
     * @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;
    }

首先校驗文件名的長度是否超出指定的長度

        int fileNamelength = file.getOriginalFilename().length();
        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
        {
            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
        }

默認的最大長度為100

    /**
     * 默認的文件名最大長度 100
     */
    public static final int DEFAULT_FILE_NAME_LENGTH = 100;

如果超出最大長度則拋出自定義異常“文件名稱超長限制異常”

public class FileNameLengthLimitExceededException extends FileException
{
    private static final long serialVersionUID = 1L;

    public FileNameLengthLimitExceededException(int defaultFileNameLength)
    {
        super("upload.filename.exceed.length", new Object[] { defaultFileNameLength });
    }
}

那么后面的代碼則不會再執行。

然后對文件大小進行校驗

assertAllowed(file, allowedExtension);

此方法的實現為

    /**
     * 文件大小校驗
     *
     * @param file 上傳的文件
     * @return
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws InvalidExtensionException
     */
    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, InvalidExtensionException
    {
        long size = file.getSize();
        if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE)
        {
            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
        }

        String fileName = file.getOriginalFilename();
        String extension = getExtension(file);
        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
        {
            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
                        fileName);
            }
            else
            {
                throw new InvalidExtensionException(allowedExtension, extension, fileName);
            }
        }

    }

默認大小為50M

    /**
     * 默認大小 50M
     */
    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;

如果超出此大小則拋出自定義異常“文件大小超出限制異常類”

/**
 * 文件大小限制異常類
 * 
 */
public class FileSizeLimitExceededException extends FileException
{
    private static final long serialVersionUID = 1L;

    public FileSizeLimitExceededException(long defaultMaxSize)
    {
        super("upload.exceed.maxSize", new Object[] { defaultMaxSize });
    }
}

那么后面的代碼也不會再被執行

然后就是獲取文件的后綴

String fileName = file.getOriginalFilename();
String extension = getExtension(file);

獲取文件后綴名的方法

    /**
     * 獲取文件名的后綴
     *
     * @param file 表單文件
     * @return 后綴名
     */
    public static final String getExtension(MultipartFile file)
    {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (StringUtils.isEmpty(extension))
        {
            extension = MimeTypeUtils.getExtension(file.getContentType());
        }
        return extension;
    }

這其中又用到

    public static String getExtension(String prefix)
    {
        switch (prefix)
        {
            case IMAGE_PNG:
                return "png";
            case IMAGE_JPG:
                return "jpg";
            case IMAGE_JPEG:
                return "jpeg";
            case IMAGE_BMP:
                return "bmp";
            case IMAGE_GIF:
                return "gif";
            default:
                return "";
        }
    }

獲取到后綴名之后就拿允許的擴展名與上傳做對比

    /**
     * 判斷MIME類型是否是允許的MIME類型
     *
     * @param extension
     * @param allowedExtension
     * @return
     */
    public static final boolean isAllowedExtension(String extension, String[] allowedExtension)
    {
        for (String str : allowedExtension)
        {
            if (str.equalsIgnoreCase(extension))
            {
                return true;
            }
        }
        return false;
    }

 

后面就是一系列的不同后綴名類型的自定義異常的拋出。

一旦有任何一種異常拋出則下面的異常就不會再被執行。

        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
        {
            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
                        fileName);
            }
            else
            {
                throw new InvalidExtensionException(allowedExtension, extension, fileName);
            }
        }

附媒體類型工具類MimeTypeUtils代碼

package com.ruoyi.common.utils.file;

/**
 * 媒體類型工具類
 *
 * @author ruoyi
 */
public class MimeTypeUtils
{
    public static final String IMAGE_PNG = "image/png";

    public static final String IMAGE_JPG = "image/jpg";

    public static final String IMAGE_JPEG = "image/jpeg";

    public static final String IMAGE_BMP = "image/bmp";

    public static final String IMAGE_GIF = "image/gif";
   
    public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };

    public static final String[] FLASH_EXTENSION = { "swf", "flv" };

    public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
            "asf", "rm", "rmvb" };

    public static final String[] DEFAULT_ALLOWED_EXTENSION = {
            // 圖片
            "bmp", "gif", "jpg", "jpeg", "png",
            // word excel powerpoint
            "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
            // 壓縮文件
            "rar", "zip", "gz", "bz2",
            // pdf
            "pdf" };

    public static String getExtension(String prefix)
    {
        switch (prefix)
        {
            case IMAGE_PNG:
                return "png";
            case IMAGE_JPG:
                return "jpg";
            case IMAGE_JPEG:
                return "jpeg";
            case IMAGE_BMP:
                return "bmp";
            case IMAGE_GIF:
                return "gif";
            default:
                return "";
        }
    }
}

 

最終拋出的無效擴展名異常InvalidExtensionException
package com.ruoyi.common.exception.file;

import java.util.Arrays;
import org.apache.commons.fileupload.FileUploadException;

/**
 * 文件上傳 誤異常類
 *
 * @author ruoyi
 */
public class InvalidExtensionException extends FileUploadException
{
    private static final long serialVersionUID = 1L;

    private String[] allowedExtension;
    private String extension;
    private String filename;

    public InvalidExtensionException(String[] allowedExtension, String extension, String filename)
    {
        super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]");
        this.allowedExtension = allowedExtension;
        this.extension = extension;
        this.filename = filename;
    }

    public String[] getAllowedExtension()
    {
        return allowedExtension;
    }

    public String getExtension()
    {
        return extension;
    }

    public String getFilename()
    {
        return filename;
    }

    public static class InvalidImageExtensionException extends InvalidExtensionException
    {
        private static final long serialVersionUID = 1L;

        public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename)
        {
            super(allowedExtension, extension, filename);
        }
    }

    public static class InvalidFlashExtensionException extends InvalidExtensionException
    {
        private static final long serialVersionUID = 1L;

        public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename)
        {
            super(allowedExtension, extension, filename);
        }
    }

    public static class InvalidMediaExtensionException extends InvalidExtensionException
    {
        private static final long serialVersionUID = 1L;

        public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename)
        {
            super(allowedExtension, extension, filename);
        }
    }
}

 

再回到upload方法

驗證文件大小沒有問題后沒有任何異常拋出則

String fileName = extractFilename(file);

對文件名進行編碼

    /**
     * 編碼文件名
     */
    public static final String extractFilename(MultipartFile file)
    {
        String fileName = file.getOriginalFilename();
        String extension = getExtension(file);
        fileName = DateUtils.datePath() + "/" + encodingFilename(fileName) + "." + extension;
        return fileName;
    }

 

在編碼文件名的方法中生成日期路徑的方法

    /**
     * 日期路徑 即年/月/日 如2018/08/08
     */
    public static final String datePath() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }

 

其中DateFormatUtils是org.apache.commons.lang3.time包中的。

然后對文件名進行編碼防止重復

    /**
     * 編碼文件名
     */
    private static final String encodingFilename(String fileName)
    {
        fileName = fileName.replace("_", " ");
        fileName = Md5Utils.hash(fileName + System.nanoTime() + counter++);
        return fileName;
    }

 

編碼方式采用MD5工具類的hash方法加上當前時間的毫秒與累加的計數器。

MD5工具類

package com.ruoyi.common.utils.security;

import java.security.MessageDigest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Md5加密方法
 *
 * @author ruoyi
 */
public class Md5Utils
{
    private static final Logger log = LoggerFactory.getLogger(Md5Utils.class);

    private static byte[] md5(String s)
    {
        MessageDigest algorithm;
        try
        {
            algorithm = MessageDigest.getInstance("MD5");
            algorithm.reset();
            algorithm.update(s.getBytes("UTF-8"));
            byte[] messageDigest = algorithm.digest();
            return messageDigest;
        }
        catch (Exception e)
        {
            log.error("MD5 Error...", e);
        }
        return null;
    }

    private static final String toHex(byte hash[])
    {
        if (hash == null)
        {
            return null;
        }
        StringBuffer buf = new StringBuffer(hash.length * 2);
        int i;

        for (i = 0; i < hash.length; i++)
        {
            if ((hash[i] & 0xff) < 0x10)
            {
                buf.append("0");
            }
            buf.append(Long.toString(hash[i] & 0xff, 16));
        }
        return buf.toString();
    }

    public static String hash(String s)
    {
        try
        {
            return new String(toHex(md5(s)).getBytes("UTF-8"), "UTF-8");
        }
        catch (Exception e)
        {
            log.error("not supported charset...{}", e);
            return s;
        }
    }
}

 

這樣經過編碼后的文件路徑就是類似下面的路徑

D:\ruoyi\uploadPath\upload\2020\09\02\6bd175725b91ec9b9f3bda0d01849479.jpg

 

 

 

編碼完文件名后就要在服務器上生成相應的目錄

    private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
    {
        File desc = new File(uploadDir + File.separator + fileName);

        if (!desc.getParentFile().exists())
        {
            desc.getParentFile().mkdirs();
        }
        if (!desc.exists())
        {
            desc.createNewFile();
        }
        return desc;
    }

 

然后將上傳的文件存儲到相應的目錄

file.transferTo(desc);

此時再獲取資源映射路徑,返回給前端用

String pathFileName = getPathFileName(baseDir, fileName);

 

此方法的實現為

    private static final String getPathFileName(String uploadDir, String fileName) throws IOException
    {
        int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
        String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
        String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
        return pathFileName;
    }

 

用到了截取字符串的方法 

   /**
     * 截取字符串
     *
     * @param str 字符串
     * @param start 開始
     * @return 結果
     */
    public static String substring(final String str, int start)
    {
        if (str == null)
        {
            return NULLSTR;
        }

        if (start < 0)
        {
            start = str.length() + start;
        }

        if (start < 0)
        {
            start = 0;
        }
        if (start > str.length())
        {
            return NULLSTR;
        }

        return str.substring(start);
    }

 

其中

    /** 空字符串 */
    private static final String NULLSTR = "";

 

截取配置的資源存放路徑的后面的相對路徑

然后拼接上項目中配置的靜態資源映射的路徑的前綴

    /**
     * 資源映射路徑 前綴
     */
    public static final String RESOURCE_PREFIX = "/profile";

 

靜態資源的映射配置類ResourcesConfig代碼

package com.ruoyi.framework.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;

/**
 * 通用配置
 *
 * @author ruoyi
 */
@Configuration
public class ResourcesConfig implements WebMvcConfigurer
{
    @Autowired
    private RepeatSubmitInterceptor repeatSubmitInterceptor;

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry)
    {
        /** 本地文件上傳路徑 */
        registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");

        /** swagger配置 */
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    /**
     * 自定義攔截規則
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry)
    {
        registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
    }

 }

 

這樣upload方法返回的pathFileName

就是在服務器上靜態資源映射好的相對路徑。

附FileUploadUtils完整代碼

package com.ruoyi.common.utils.file;

import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.security.Md5Utils;
import com.ruoyi.framework.config.RuoYiConfig;

/**
 * 文件上傳工具類
 *
 * @author ruoyi
 */
public class FileUploadUtils
{
    /**
     * 默認大小 50M
     */
    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;

    /**
     * 默認的文件名最大長度 100
     */
    public static final int DEFAULT_FILE_NAME_LENGTH = 100;

    /**
     * 默認上傳的地址
     */
    private static String defaultBaseDir = RuoYiConfig.getProfile();

    private static int counter = 0;

    public static void setDefaultBaseDir(String defaultBaseDir)
    {
        FileUploadUtils.defaultBaseDir = defaultBaseDir;
    }

    public static String getDefaultBaseDir()
    {
        return defaultBaseDir;
    }

    /**
     * 以默認配置進行文件上傳
     *
     * @param file 上傳的文件
     * @return 文件名稱
     * @throws Exception
     */
    public static final String upload(MultipartFile file) throws IOException
    {
        try
        {
            return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        }
        catch (Exception e)
        {
            throw new IOException(e.getMessage(), e);
        }
    }

    /**
     * 根據文件路徑上傳
     *
     * @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 extension 上傳文件類型
     * @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;
    }

    /**
     * 編碼文件名
     */
    public static final String extractFilename(MultipartFile file)
    {
        String fileName = file.getOriginalFilename();
        String extension = getExtension(file);
        fileName = DateUtils.datePath() + "/" + encodingFilename(fileName) + "." + extension;
        return fileName;
    }

    private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
    {
        File desc = new File(uploadDir + File.separator + fileName);

        if (!desc.getParentFile().exists())
        {
            desc.getParentFile().mkdirs();
        }
        if (!desc.exists())
        {
            desc.createNewFile();
        }
        return desc;
    }

    private static final String getPathFileName(String uploadDir, String fileName) throws IOException
    {
        int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
        String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
        String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
        return pathFileName;
    }

    /**
     * 編碼文件名
     */
    private static final String encodingFilename(String fileName)
    {
        fileName = fileName.replace("_", " ");
        fileName = Md5Utils.hash(fileName + System.nanoTime() + counter++);
        return fileName;
    }

    /**
     * 文件大小校驗
     *
     * @param file 上傳的文件
     * @return
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws InvalidExtensionException
     */
    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, InvalidExtensionException
    {
        long size = file.getSize();
        if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE)
        {
            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
        }

        String fileName = file.getOriginalFilename();
        String extension = getExtension(file);
        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
        {
            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
                        fileName);
            }
            else
            {
                throw new InvalidExtensionException(allowedExtension, extension, fileName);
            }
        }

    }

    /**
     * 判斷MIME類型是否是允許的MIME類型
     *
     * @param extension
     * @param allowedExtension
     * @return
     */
    public static final boolean isAllowedExtension(String extension, String[] allowedExtension)
    {
        for (String str : allowedExtension)
        {
            if (str.equalsIgnoreCase(extension))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 獲取文件名的后綴
     *
     * @param file 表單文件
     * @return 后綴名
     */
    public static final String getExtension(MultipartFile file)
    {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (StringUtils.isEmpty(extension))
        {
            extension = MimeTypeUtils.getExtension(file.getContentType());
        }
        return extension;
    }

 }

 

再回到接口Controller,還需要拼接上當前服務器的域名和ip

 String url = serverConfig.getUrl() + fileName;

 

調用了服務器相關配置的獲取完整的請求路徑的方法

package com.ruoyi.framework.config;

import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.ServletUtils;

/**
 * 服務相關配置
 *
 * @author ruoyi
 */
@Component
public class ServerConfig
{
    /**
     * 獲取完整的請求路徑,包括:域名,端口,上下文訪問路徑
     *
     * @return 服務地址
     */
    public String getUrl()
    {
        HttpServletRequest request = ServletUtils.getRequest();
        return getDomain(request);
    }

    public static String getDomain(HttpServletRequest request)
    {
        StringBuffer url = request.getRequestURL();
        String contextPath = request.getServletContext().getContextPath();
        return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
    }

}

 

在獲取請求對象時調用了客戶端工具類的ServletUtils.getRequest();

   /**
     * 獲取request
     */
    public static HttpServletRequest getRequest()
    {
        return getRequestAttributes().getRequest();
    }

 

其中用到了

    public static ServletRequestAttributes getRequestAttributes()
    {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    } 

 

其中RequestContextHolder在

org.springframework.web.context.request下

最終后台接口返回的url是一個在服務器上絕對路徑的圖片地址,比如

http://localhost:8080/profile/upload/2020/09/02/e070fd1a26dca6c00acf6db1bc467905.png

接口返回時AjaxResult是自定義的響應類

package com.ruoyi.framework.web.domain;

import java.util.HashMap;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.utils.StringUtils;

/**
 * 操作消息提醒
 *
 * @author ruoyi
 */
public class AjaxResult extends HashMap<String, Object>
{
    private static final long serialVersionUID = 1L;

    /** 狀態碼 */
    public static final String CODE_TAG = "code";

    /** 返回內容 */
    public static final String MSG_TAG = "msg";

    /** 數據對象 */
    public static final String DATA_TAG = "data";

    /**
     * 初始化一個新創建的 AjaxResult 對象,使其表示一個空消息。
     */
    public AjaxResult()
    {
    }

    /**
     * 初始化一個新創建的 AjaxResult 對象
     *
     * @param code 狀態碼
     * @param msg 返回內容
     */
    public AjaxResult(int code, String msg)
    {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
    }

    /**
     * 初始化一個新創建的 AjaxResult 對象
     *
     * @param code 狀態碼
     * @param msg 返回內容
     * @param data 數據對象
     */
    public AjaxResult(int code, String msg, Object data)
    {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
        if (StringUtils.isNotNull(data))
        {
            super.put(DATA_TAG, data);
        }
    }

    /**
     * 返回成功消息
     *
     * @return 成功消息
     */
    public static AjaxResult success()
    {
        return AjaxResult.success("操作成功");
    }

    /**
     * 返回成功數據
     *
     * @return 成功消息
     */
    public static AjaxResult success(Object data)
    {
        return AjaxResult.success("操作成功", data);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回內容
     * @return 成功消息
     */
    public static AjaxResult success(String msg)
    {
        return AjaxResult.success(msg, null);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回內容
     * @param data 數據對象
     * @return 成功消息
     */
    public static AjaxResult success(String msg, Object data)
    {
        return new AjaxResult(HttpStatus.SUCCESS, msg, data);
    }

    /**
     * 返回錯誤消息
     *
     * @return
     */
    public static AjaxResult error()
    {
        return AjaxResult.error("操作失敗");
    }

    /**
     * 返回錯誤消息
     *
     * @param msg 返回內容
     * @return 警告消息
     */
    public static AjaxResult error(String msg)
    {
        return AjaxResult.error(msg, null);
    }

    /**
     * 返回錯誤消息
     *
     * @param msg 返回內容
     * @param data 數據對象
     * @return 警告消息
     */
    public static AjaxResult error(String msg, Object data)
    {
        return new AjaxResult(HttpStatus.ERROR, msg, data);
    }

    /**
     * 返回錯誤消息
     *
     * @param code 狀態碼
     * @param msg 返回內容
     * @return 警告消息
     */
    public static AjaxResult error(int code, String msg)
    {
        return new AjaxResult(code, msg, null);
    }
}

這樣講就能實現通用文件上傳接口並返回在服務器上能訪問到的路徑。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM