SpringBoot實現(xiàn)本地上傳文件到resources目錄
需求背景:Java后端項目上傳文件是一個很常見的需求,一般正式項目中我們上傳文件都是利用第三方阿里云OSS這類的,但是如果只是為了學習之用,那我們可能就會直接上傳到電腦上某個本地文件夾。
但是上傳到自己電腦上某個文件夾,那換一臺電腦就看不到了,還有一般文件上傳之后我們還需要返回給前端文件的下載路徑,如果是電腦上隨便某個文件夾,那前端很可能是訪問不到的。
為了解決前端訪問這個問題,我們可以把文件上傳到后端服務的靜態(tài)資源目錄里,這樣前端就可以直接通過后端服務的地址和端口加上資源路徑來訪問了。
實現(xiàn)思路
上傳文件的路徑我們可以用 ResourceUtils.getURL("classpath:").getPath() 這個方法來獲取,拿到的就是編譯后的 target/classes 目錄的絕對路徑,前端上傳的文件就可以直接存到這個下面的目錄,比如:target/classes/upload/logo.jpg,給前端返回的下載地址就像這樣的:http://localhost:8080/upload/logo.jpg。
上面的思路確實解決了上傳和下載的問題,但是 target 目錄是會變動的,而且不會提交到代碼倉庫,如果我們清理后再重新編譯或者換臺電腦編譯,之前上傳的文件就都沒了。
這可怎么辦呢?仔細一想我們項目不是有一個叫 resources 用來存放靜態(tài)資源的目錄嗎,這個目錄正常也會提交到代碼倉庫進行管理的,那我們每次上傳的文件不就可以一塊提交到倉庫里,這部就實現(xiàn)了永久保存。
說干就干,就直接將文件保存到 resources/upload 目錄下,后端一run前端一上傳,文件確實被保存到了 resources/upload 目錄下。再仔細一看不對,前端的地址沒發(fā)訪問剛上傳的文件,因為 target/classes 目錄下壓根沒有剛上傳的文件,重新點一次 compile 編譯后將 resources 目錄下的文件同步到了 target/classes 目錄下確實可以實現(xiàn)訪問,但是總不能我們每次上傳后都要自己重新點一下編譯重新運行吧。
最后一合計,那我把resources和target結合一下,將文件同時保存到這兩個目錄下,是不是就可以實現(xiàn)永久保存和實時訪問了呢。
終極方案
用System.getProperty("user.dir")可以獲取到項目的工作目錄,再拼上項目的結構目錄就可以拿到 resources 目錄的絕對路徑;target/classes 運行目錄可以用 ResourceUtils.getURL("classpath:").getPath() 獲取。
注意如果最后上傳的資源目錄訪問404,要看下 application.yml 里 spring.mvn 的靜態(tài)資源路徑,pom.xml里的 resources過濾規(guī)則,還有 WebMvcConfiguration 里配置的 addResourceHandler 靜態(tài)資源攔映射有沒有攔截掉。
最后前端傳過來的是一個 File 文件,但是一個文件其實是沒辦法循環(huán)去保存到多個目錄下的,第一個文件夾保存成功后后面的都會報錯,想一下我們平時在電腦上保存一個文件也只能保存到一個目錄下,再要保存到其他目錄則自己復制一份過去就好了,這里也是一樣第一個目錄我們直接保存,第二個則可以用 spring 提供的 FileCopyUtils.copy 直接復制文件就可以了。
完整代碼
UploadFileUtil.java
package com.sky.utils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;
public class UploadFileUtil {
/**
* 獲取文件保存路徑
*
* @return File
* @throws FileNotFoundException
*/
static List<File> getUploadDirectory() throws FileNotFoundException {
// 開發(fā)環(huán)境獲取 target/classes 目錄:清理重新編譯后就沒有了
File targetPath = new File(ResourceUtils.getURL("classpath:").getPath());
// System.out.printf("項目運行的絕對路徑:" + path.getAbsolutePath());
// 輸出 xx/sky-parent/sky-server/target/classes
// 生產(chǎn)環(huán)境 不存在 target/classes 目錄
if (!targetPath.exists()) {
// 獲取當前運行目錄
targetPath = new File("");
}
// 開發(fā)環(huán)境 resources 目錄:可永久保存
String resourcesPath = System.getProperty("user.dir") + "/sky-server/src/main/resources";
// System.out.printf("resources目錄路徑:" + resourcesPath);
File path = new File(resourcesPath);
File upload = new File(path.getAbsolutePath(), "upload");
File uploadTarget = new File(targetPath.getAbsolutePath(), "upload");
// 不存在則創(chuàng)建
if (!upload.exists()) {
upload.mkdirs();
}
if (!uploadTarget.exists()) {
uploadTarget.mkdirs();
}
List<File> files = new ArrayList<File>();
files.add(upload);
files.add(uploadTarget);
// System.out.printf("當前目錄:" + files);
return files;
}
public static String upload(MultipartFile myFile, String dir) throws IOException {
String filePath = "";
if (!myFile.isEmpty()) {
try {
String filename = myFile.getOriginalFilename();
filename = UUID.randomUUID() + filename.substring(filename.lastIndexOf("."));
// 之所以保存到 resources 和 target 兩個目錄,兼顧開發(fā)測試和永久保存
// 只保存到resources目錄下每次上傳了要重新編譯下,target則清理打包后就沒有了
List<File> files = getUploadDirectory();
// 注意這里一個文件不能循環(huán)同時寫入多個目錄,保存了第一個,第二個要復制過去
File curFile = new File(files.get(0), filename);
myFile.transferTo(curFile);
FileCopyUtils.copy(curFile, new File(files.get(1), filename));
//for (File f: files) {
//File curFile = new File(f, filename);
//myFile.transferTo(curFile);
//}
filePath = "http://localhost:8080/upload/" + filename;
} catch (Exception e) {
e.printStackTrace();
}
}
return filePath;
}
}application.yml
server:
port: 8080
spring:
mvc:
static-path-pattern: /upload/**WebMvcConfiguration
package com.sky.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
/**
* 配置類,注冊web層相關組件
*/
@Configuration
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
/**
* 設置靜態(tài)資源映射
* @param registry
*/
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
// 設置上傳的文件靜態(tài)資源映射,application 里的 mvc 里也要設置下靜態(tài)目錄
registry.addResourceHandler("/upload/**")
.addResourceLocations("classpath:/upload/", "file:upload/");
}
}使用示例
在 controller 接收前端用表單上傳的 File 文件
package com.sky.controller.common;
import com.sky.result.Result;
import com.sky.utils.UploadFileUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* 公共請求
*/
@RestController
@RequestMapping("/common")
@Api(tags = "公共")
@Slf4j
public class CommonController {
@PostMapping("/upload")
@ApiOperation("上傳文件")
public Result uploadFile(MultipartFile file) throws IOException {
log.info("上傳文件:{}", file);
String fileUrl = UploadFileUtil.upload(file, "");
if (fileUrl == null || fileUrl == "") {
return Result.error("上傳失敗");
}
return Result.success(fileUrl);
}
}到此這篇關于SpringBoot實現(xiàn)本地上傳文件到resources目錄的文章就介紹到這了,更多相關SpringBoot上傳文件resources目錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Springboot 項目讀取Resources目錄下的文件(推薦)
- 解決springboot項目找不到resources目錄下的資源問題
- 解決@springboottest注解無法加載src/main/resources目錄下文件
- springboot項目讀取resources目錄下的文件的9種方式
- springboot實現(xiàn)jar運行復制resources文件到指定的目錄(思路詳解)
- SpringBoot中讀取jar包中的resources目錄下的文件的三種方式
- SpringBoot如何讀取resources目錄下的文件
- Springboot獲取jar包中resources資源目錄下的文件
- Springboot項目啟動不加載resources目錄下的文件問題
- SpringBoot下獲取resources目錄下文件的常用方法
相關文章
在java中實現(xiàn)C#語法里的按引用傳遞參數(shù)的方法
下面小編就為大家?guī)硪黄趈ava中實現(xiàn)C#語法里的按引用傳遞參數(shù)的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-09-09
Spring Security的持久化用戶和授權實現(xiàn)方式
文章介紹了如何使用JdbcUserDetailsManager實現(xiàn)數(shù)據(jù)庫讀取用戶,并展示了如何配置SpringSecurity進行授權管理,通過創(chuàng)建數(shù)據(jù)庫表、配置數(shù)據(jù)庫連接和修改SecurityConfig,實現(xiàn)了用戶權限的控制2025-02-02
Spring中@Configuration注解和@Component注解的區(qū)別詳解
這篇文章主要介紹了Spring中@Configuration注解和@Component注解的區(qū)別詳解,@Configuration 和 @Component 到底有何區(qū)別呢?我先通過如下一個案例,在不分析源碼的情況下,小伙伴們先來直觀感受一下這兩個之間的區(qū)別,需要的朋友可以參考下2023-09-09
高并發(fā)計數(shù)器LongAdder 實現(xiàn)原理與使用場景詳解
這篇文章主要介紹了高并發(fā)計數(shù)器LongAdder 實現(xiàn)原理與使用場景詳解,通過理解 LongAdder 的設計哲學和實現(xiàn)細節(jié),開發(fā)者可以在高并發(fā)場景中做出更優(yōu)的技術選型,在保證線程安全的前提下實現(xiàn) 5-10 倍的性能提升,需要的朋友可以參考下2025-06-06

