Java中的MultipartFile接口和File類解讀
一、File類
java.io.File是 Java 標(biāo)準(zhǔn)庫中用于操作文件和目錄路徑的類。它提供了很多方法,用于創(chuàng)建、刪除、重命名、判斷文件是否存在、獲取文件信息等操作。
獲取文件信息
boolean exists(): 判斷文件或目錄是否存在。boolean isFile(): 判斷是否是文件。boolean isDirectory(): 判斷是否是目錄。String getName(): 獲取文件或目錄的名稱。String getPath(): 獲取文件或目錄的路徑。String getAbsolutePath(): 獲取文件或目錄的絕對路徑。long length(): 獲取文件的大?。ㄗ止?jié)數(shù))。
文件和目錄操作
boolean createNewFile(): 創(chuàng)建新文件。如果文件已存在,則不創(chuàng)建,返回 false。boolean mkdir(): 創(chuàng)建新目錄。如果目錄已存在,則不創(chuàng)建,返回 false。boolean mkdirs(): 創(chuàng)建新目錄及其父目錄,如果不存在的話。boolean delete(): 刪除文件或目錄。
文件路徑操作
boolean renameTo(File dest): 重命名文件或目錄。如果成功,返回 true;否則,返回 false。String[] list(): 返回目錄下的文件和目錄名數(shù)組。File[] listFiles(): 返回目錄下的文件和目錄的 File 對象數(shù)組。
文件過濾
String[] list(FilenameFilter filter): 返回目錄下滿足指定過濾器條件的文件和目錄名數(shù)組。File[] listFiles(FileFilter filter): 返回目錄下滿足指定過濾器條件的文件和目錄的 File 對象數(shù)組。
二、MultipartFile接口
MultipartFile是 Spring 框架提供的一個接口,用于表示處理文件上傳的對象。
它通常用于處理multipart/form-data類型的請求,例如處理文件上傳的表單。
首先我們依舊可以通過源碼的學(xué)習(xí)來進一步了解這個接口。
2.1 源碼和方法功能
public interface MultipartFile extends InputStreamSource {
String getName();
@Nullable
String getOriginalFilename();
@Nullable
String getContentType();
boolean isEmpty();
long getSize();
byte[] getBytes() throws IOException;
InputStream getInputStream() throws IOException;
default Resource getResource() {
return new MultipartFileResource(this);
}
void transferTo(File dest) throws IOException, IllegalStateException;
default void transferTo(Path dest) throws IOException, IllegalStateException {
FileCopyUtils.copy(this.getInputStream(), Files.newOutputStream(dest));
}
}String getName():獲取上傳文件的表單字段名稱String getOriginalFilename():獲取上傳文件的原始文件名String getContentType():獲取上傳文件的內(nèi)容類型boolean isEmpty():判斷上傳文件是否為空long getSize():獲取上傳文件的大小,單位是字節(jié)byte[] getBytes() throws IOException:獲取上傳文件的字節(jié)數(shù)組表示InputStream getInputStream() throws IOException:獲取上傳文件的輸入流default Resource getResource():將 MultipartFile 封裝成了 Resource 對象,從而可以使用 Resource 接口提供的方法來操作上傳文件的內(nèi)容。void transferTo(File dest) throws IOException,IllegalStateException:將上傳文件保存到指定的文件;default void transferTo(Path dest) throws IOException,IllegalStateException:將上傳文件保存在指定的路徑下;
2.2 void transferTo(File dest)
前面我們已經(jīng)介紹了該方法是Spring中提供的將上傳文件保存到指定的文件中的抽象方法,溯源源碼我們可以看到這個接口方法被三個實現(xiàn)類實現(xiàn)了,分別是CommonsMultipartFile、MockMultipartFile 和 StandardMultipartHttpServletRequest。
CommonsMultipartFile中的方法體
我們可以看到CommonsMultipartFile中的方法體主要是通過檢測傳進來的文件是否可用、是否存在,并在檢測完成就執(zhí)行寫入的操作
public void transferTo(File dest) throws IOException, IllegalStateException {
if (!this.isAvailable()) {
throw new IllegalStateException("File has already been moved - cannot be transferred again");
} else if (dest.exists() && !dest.delete()) {
throw new IOException("Destination file [" + dest.getAbsolutePath() + "] already exists and could not be deleted");
} else {
try {
this.fileItem.write(dest);
LogFormatUtils.traceDebug(logger, (traceOn) -> {
String action = "transferred";
if (!this.fileItem.isInMemory()) {
action = this.isAvailable() ? "copied" : "moved";
}
return "Part '" + this.getName() + "', filename '" + this.getOriginalFilename() + "'" + (traceOn ? ", stored " + this.getStorageDescription() : "") + ": " + action + " to [" + dest.getAbsolutePath() + "]";
});
} catch (FileUploadException var3) {
throw new IllegalStateException(var3.getMessage(), var3);
} catch (IOException | IllegalStateException var4) {
throw var4;
} catch (Exception var5) {
throw new IOException("File transfer failed", var5);
}
}
}上面這段demo中可能對于this.isAvailable()有疑問,我們知曉這里的this其實是該類的實例化對象,但是這里的this.isAvailable()就是拿來判斷目的文件是否可用,調(diào)用的就是類的內(nèi)部方法,判斷是否可用的條件就是該目標(biāo)文件是否被加載進內(nèi)存中!

這里給出一個使用該方法的例子,需要注意的是這時候方法要求的是一個目標(biāo)File對象,我們需要在調(diào)用該目標(biāo)方法的時候就根據(jù)目標(biāo)路徑創(chuàng)建了目標(biāo)的File對象。
// 獲取上傳文件的原始文件名
String originalFilename = StringUtils.cleanPath(file.getOriginalFilename());
// 構(gòu)建目標(biāo)文件對象
File destFile = new File("/path/to/destination/directory", originalFilename);
try {
// 將上傳文件保存到目標(biāo)文件
file.transferTo(destFile);
return "File uploaded successfully!";
} catch (IOException e) {
e.printStackTrace();
return "Failed to upload the file.";
}StandardMultipartHttpServletRequest實現(xiàn)類
而另一個實現(xiàn)類StandardMultipartHttpServletRequest和CommonsMultipartFile的區(qū)別就在于使用StandardMultipartHttpServletRequest直接上傳文件的話可能會出現(xiàn)目錄跳躍的問題,而CommonsMultipartFile不會,這是因為其對路勁分隔符了相關(guān)的限制。

default void transferTo(Path dest)
該默認(rèn)方法在實現(xiàn)類中被重寫了,但主要的功能還是不變,就是將上傳的文件寫入到指定路徑的Path對象中實現(xiàn)文件上傳的功能。
這里給出使用的示例代碼:
// 構(gòu)建目標(biāo)文件路徑
String uploadDirectory = "/path/to/destination/directory";
String originalFilename = file.getOriginalFilename();
Path filePath = Paths.get(uploadDirectory, originalFilename);
try {
// 將上傳文件保存到目標(biāo)文件
file.transferTo(filePath);
return "File uploaded successfully!";
} catch (IOException e) {
e.printStackTrace();
return "Failed to upload the file.";
}實際項目使用文件上傳和下載
@PostMapping("/file/upload")
public Result uploadFile(MultipartFile file) {
String originalFilename = file.getOriginalFilename();
if (StrUtil.isBlank(originalFilename)) {
return Result.error("文件上傳失敗");
}
long flag = System.currentTimeMillis();
String filePath = BASE_FILE_PATH + flag + "_" + originalFilename;
try {
FileUtil.mkParentDirs(filePath); // 創(chuàng)建父級目錄
file.transferTo(FileUtil.file(filePath));
Admin currentAdmin = TokenUtils.getCurrentAdmin();
String token = TokenUtils.genToken(currentAdmin.getId().toString(), currentAdmin.getPassword(), 15);
String url = "http://localhost:9090/api/book/file/download/" + flag + "?&token=" + token;
if (originalFilename.endsWith("png") || originalFilename.endsWith("jpg") || originalFilename.endsWith("pdf")) {
url += "&play=1";
}
return Result.success(url);
} catch (Exception e) {
log.info("文件上傳失敗", e);
}
return Result.error("文件上傳失敗");
}
@GetMapping("/file/download/{flag}")
public void download(@PathVariable String flag, @RequestParam(required = false) String play, HttpServletResponse response) {
OutputStream os;
List<String> fileNames = FileUtil.listFileNames(BASE_FILE_PATH);
String fileName = fileNames.stream().filter(name -> name.contains(flag)).findAny().orElse(""); // System.currentTimeMillis() + originalFilename
try {
if (StrUtil.isNotEmpty(fileName)) {
String realName = fileName.substring(fileName.indexOf("_") + 1);
if ("1".equals(play)) {
response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(realName, "UTF-8"));
} else {
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(realName, "UTF-8"));
}
byte[] bytes = FileUtil.readBytes(BASE_FILE_PATH + fileName);
os = response.getOutputStream();
os.write(bytes);
os.flush();
os.close();
}
} catch (Exception e) {
log.error("文件下載失敗", e);
}
}文件上傳接口
1.接口設(shè)置
- @postMapping(“file/upload”):這個注解表明這個方法處理HTTP POST請求,請求路徑是/file/upload。
- Public Result uploadFile(Multipartfile file):Result是一個自定義的響應(yīng)對象,包含了操作成功或操作失敗的狀態(tài),以及相關(guān)數(shù)據(jù)。
2.文件處理
- String originalFilename = file.getOriginalFilename():獲取上傳文件的原始文件名。
- if (StrUtil.isBlank(originalFilename)):檢查文件名是否為空,如果為空,返回錯誤響應(yīng)(Result.error)。
- long flag = System.currentTimeMillis():獲取當(dāng)前時間戳(毫秒),用于創(chuàng)建唯一的文件名。
- String filePath = BASE_FILE_PATH + flag + "_" + originalFilename:構(gòu)建完整的文件保存路徑。BASE_FILE_PATH指存儲文件的根目錄,文件名由時間戳和原始文件名拼接而成。
3.文件保存
- FileUtil.mkParentDirs(filePath):確保所有父目錄都存在,如果不存在,則創(chuàng)建它們。
- file.transferTo(FileUtil.file(filePath)):這行關(guān)鍵代碼將上傳的文件內(nèi)容保存到服務(wù)器上的filePath路徑。
4.令牌生成(可選)
- Admin currentAdmin = TokenUtils.getCurrentAdmin():獲取當(dāng)前登錄的管理員用戶,這意味著實現(xiàn)了身份驗證。
- String token = TokenUtils.genToken(currentAdmin.getId().toString(), currentAdmin.getPassword(), 15):根據(jù)管理員ID、密碼和15分鐘的過期時間生成一個jwt令牌。
5.URL構(gòu)建
- String url = "http://localhost:9090/api/book/file/download/" + flag + "?&token=" + token:構(gòu)建一個指向下載接口(/api/book/file/download/)的URL,包含時間戳(flag)和生成的令牌。
- if(originalFilename.endsWith("png")||originalFilename.endsWith("jpg") || originalFilename.endsWith("pdf")):如果是圖片(png、jpg)或pdf文件,則在URL后面添加&play=1,這表示服務(wù)器可能提供文件預(yù)覽功能。
6.響應(yīng)
- return Result.success(url):上傳成功后,返回包含下載的Result.success(url)響應(yīng)
- catch (Exception e):處理過程中可能出現(xiàn)的異常,記錄錯誤日志并返回一個通用的Result.error響應(yīng)。
文件下載接口
1.注解
- (1)@GetMapping("/file/download/{flag}"):使用GetingMapping注解處理Get請求,路徑包含一個變量{flag},將被捕獲并傳遞給方法。
- (2)@PathVariable String flag:此注解將{flag}路徑變量的值綁定到方法的flag參數(shù)。
- (3)@RequestParam(required = false) String play:此注解將可選查詢參數(shù)”play”的值綁定到方法的play參數(shù)。required=false使此參數(shù)成為可選參數(shù)。
2.方法參數(shù):
- (1)String flag:此參數(shù)將包含作為URL中{flag}傳遞的值。它用于標(biāo)識要下載的文件。
- (2)String play:此可選參數(shù)(來自查詢字符串)可用于控制下載行為。如果其值為“1”,則文件可能會直接在瀏覽器中打開(內(nèi)聯(lián))。否則,它將被視為常規(guī)下載。
- (3)HttpServletResponse response:此對象用于操作發(fā)送回客戶端的HTTP響應(yīng)。
3.文件處理
- (1)List<String> fileNames = FileUtil.listFileNames(BASE_FILE_PATH):此行假設(shè)存在一個FileUtil類,其中包含一個listFileNames方法,該方法從BASE_FILE_PATH指定的目錄中檢索文件名列表。
- (2)String fileName = fileNames.stream().filter(name -> name.contains(flag)).findAny().orElse(""):此行使用Java流查找包含提供的flag的文件名。
- (3)String realName = fileName.substring(fileName.indexOf("_") + 1):通過刪除找到的文件名中的任何前綴(第一個”_”之前)來提取實際文件名。
響應(yīng)處理
if ("1".equals(play)) {
response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(realName, "UTF-8"));
} else {
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(realName, "UTF-8"));
}
檢查play參數(shù)的值。如果play為“1”,則將Content-Disposition標(biāo)頭設(shè)置為"inline;filename=" + URLEncoder.encode(realName, "UTF-8"),這通常告訴瀏覽器嘗試直接打開文件。否則,他會將標(biāo)頭設(shè)置為 "attachment;filename=" + URLEncoder.encode(realName, "UTF-8"),這通常會強制瀏覽器下載文件。
- URLEncoder.encode(realName, "UTF-8")使用UTF-8編碼對文件名進行編碼,以正確處理文件名中的特殊字符和空格。
- byte[] bytes = FileUtil.readBytes(BASE_FILE_PATH + fileName);使用FileUtil類中的另一個假設(shè)方法將文件內(nèi)容讀入字節(jié)數(shù)組。
- os = response.getOutputStream():從response對象獲取輸出流,允許代碼將數(shù)據(jù)直接寫入HTTP響應(yīng)體。
- os.write(bytes);os.flush(); os.close();:將文件內(nèi)容(字節(jié)數(shù)組)寫入輸出流,刷新流以確保發(fā)送所有數(shù)據(jù),然后關(guān)閉流。
- catch (Exception e) { log.error("文件下載失敗", e);}:此catch塊捕獲在文件下載過程中拋出的任何異常,并記錄錯誤消息。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java從ftp服務(wù)器上傳與下載文件的實現(xiàn)
這篇文章主要給大家介紹了關(guān)于Java從ftp服務(wù)器上傳與下載文件的實現(xiàn)方法,最近項目中需要實現(xiàn)將文件先存放到ftp上,需要的時候再從ftp上下載,做的過程中碰到了問題,所以這里總結(jié)下,需要的朋友可以參考下2023-08-08
java工廠實例BeanFactoryPostProcessor和BeanPostProcessor區(qū)別分析
這篇文章主要為大家介紹了BeanFactoryPostProcessor和BeanPostProcessor區(qū)別示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07
SpringBoot如何利用Twilio?Verify發(fā)送驗證碼短信
Twilio提供了一個名為?Twilio?Verify?的服務(wù),專門用于處理驗證碼的發(fā)送和驗證,下面我們就來看看如何使用Twilio?Verify實現(xiàn)發(fā)送驗證碼短信吧2025-03-03
Java代碼實現(xiàn)哈希表(google 公司的上機題)
這篇文章主要介紹了Java 哈希表詳解(google 公司的上機題),本文通過圖文實例相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03
SpringBoot整合FastDFS中間件實現(xiàn)文件分布管理
FastDFS是一個開源的輕量級分布式文件系統(tǒng),它對文件進行管理,功能包括:文件存儲、文件同步、文件上傳、文件下載等,解決了大容量存儲和負(fù)載均衡的問題,本文介紹了SpringBoot整合FastDFS中間件實現(xiàn)文件分布管理,需要的朋友可以參考下2024-08-08
MyBatis-Plus 與Druid 數(shù)據(jù)源操作
SpringBoot框架集成MyBatis-Plus和Druid數(shù)據(jù)源,簡化了數(shù)據(jù)操作與監(jiān)控,MyBatis-Plus作為MyBatis的增強工具,自動實現(xiàn)CRUD操作,減少手寫SQL,提供分頁、邏輯刪除等功能,本文介紹MyBatis-Plus & Druid 數(shù)據(jù)源總結(jié),感興趣的朋友一起看看吧2024-09-09
MyBatisPlus利用Service實現(xiàn)獲取數(shù)據(jù)列表
這篇文章主要為大家詳細(xì)介紹了怎樣使用 IServer 提供的 list 方法查詢多條數(shù)據(jù),這些方法將根據(jù)查詢條件獲取多條數(shù)據(jù),感興趣的可以了解一下2022-06-06

