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 // 生產環(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目錄內容請搜索腳本之家以前的文章或繼續(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目錄下文件的常用方法
相關文章
Spring Data JPA中的Specification動態(tài)查詢詳解
Specification是一個設計模式,用于企業(yè)級應用開發(fā)中,其主要目的是將業(yè)務規(guī)則從業(yè)務邏輯中分離出來,在數(shù)據(jù)查詢方面,Specification可以定義復雜的查詢,使其更易于重用和測試,這篇文章主要介紹了Spring Data JPA中的Specification動態(tài)查詢詳解,需要的朋友可以參考下2023-07-07SpringBoot通過JSON傳遞請求參數(shù)的實例詳解
這篇文章主要介紹了SpringBoot通過JSON傳遞請求參數(shù),示例介紹SpringMVC如何通過JSON格式傳遞入?yún)?,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-11-11Java在PowerPoint中添加上標和下標的實現(xiàn)方法
當我們在演示文稿中添加商標、版權或其他符號時,我們可能希望該符號出現(xiàn)在某個文本的上方或下方。在Microsoft PowerPoint中,我們可以通過對符號應用上標或下標格式來實現(xiàn)這種效果,這篇文章主要介紹了Java在PowerPoint中添加上標和下標,需要的朋友可以參考下2022-10-10Springboot實現(xiàn)多線程注入bean的工具類操作
這篇文章主要介紹了Springboot實現(xiàn)多線程注入bean的工具類操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08創(chuàng)建SpringBoot工程并集成Mybatis的方法
這篇文章主要介紹了創(chuàng)建SpringBoot工程并集成Mybatis,需要的朋友可以參考下2018-06-06