1.情景展示
文件上傳,在開發過程中,經常會用到,springboot如何接收上傳文件?
本文將以springboot為例,如何接收前端的文件請求;
上傳文件使用插件更方便,這里我使用webuploader。
2.前端代碼
第一步:准備webuploader插件放到項目當中;
由於,springboot在項目啟動的時候,會自動將static目錄下的靜態資源(前端代碼)加載到項目當中;
所以,我這里為了省事兒,就不創建web目錄了;
而且springboot還會自動將resources/static目錄下的index.html當做項目的歡迎頁(我這里為了省事,就沒有配置請求與頁面想對照的映射關系)。
現在,我們來對前端代碼進行整體認知;
一個要存放插件的位置:webuploader;
一個要展示插件的頁面:index.html;
一個要處理文件上傳的js:upload.js;
index.html需要引入:
webuploader.css和webuploader.js;
必須在webuploader.js引入之前引入jQuery.js(因為它依賴jQuery);
include.js包含的內容如下:
// 獲取請求路徑
var pathName = window.document.location.pathname;
// 通過截取得到項目名稱
var baseUrl = pathName.substring(0, pathName.substr(1).indexOf('/') + 1);
用途:獲取該項目的名稱。
html的核心代碼為:
<div id="filePicker" style="line-height:15px;" class="webuploader-container">
<div class="webuploader-pick">選擇文件</div>
</div>
webuploader插件會通過init()方法,對filePicker這個div里面追加內容,例如:
js核心代碼:
查看代碼
/**
* 該插件批量上傳的本質:
* 每個文件單獨發送一個上傳請求,所以,其本質還是單文件上傳,而不是真正含義上的:一次請求包含多個文件。
*/
this.init = function () {
var state = 'pending';
var upUrl = baseUrl + '/file/upload.do';
var uploader = WebUploader.create({
auto: true, // 選擇文件后自動上傳,默認不自動上傳需要觸發
swf: baseUrl + '/webuploader/Uploader.swf', // swf文件路徑
server: upUrl, // 上傳文件的接口(替換成你們后端給的接口路徑)
// 選擇文件的按鈕。可選。
// 內部根據當前運行是創建,可能是input元素,也可能是flash.
pick: '#filePicker',
accept: {
//extensions: 'xls,xlsx', // 允許的文件后綴,不帶點,多個用逗號分割,這里支持老版的Excel和新版的
mimeTypes: 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
// 說明:extensions沒用,不生效
// 只有mimeTypes的值對應文件域的accept屬性,僅能通過它來控制上傳文件的類型
// 文件類型,也可以通過“.后綴名”的方式來實現,如:.xls,.xlsx
},
resize: false, // 不壓縮image, 默認如果是jpeg,文件上傳前會壓縮一把再上傳!
/* duplicate :true */ //可重復上傳
});
/**
* 文件被加入隊列之前觸發
* @explain 主要用來傳其它參數
*/
uploader.on('beforeFileQueued', function (file) {
var obj = new Object();
obj.THEMEID = $('#THEMEID').val();
uploader.options.formData = obj;
});
/**
* 上傳成功
*/
uploader.on('uploadSuccess', function( file, result) {
alert(JSON.stringify(result));
});
/**
* 上傳失敗
*/
uploader.on('uploadError', function( file, result) {
// alert('文件上傳失敗!');
alert(JSON.stringify(result));
});
/**
* 上傳完成
*/
uploader.on( 'uploadComplete', function( file ) {
});
uploader.on( 'all', function( type ) {
if ( type === 'startUpload' ) {
state = 'uploading';
} else if ( type === 'stopUpload' ) {
state = 'paused';
} else if ( type === 'uploadFinished' ) {
state = 'done';
}
});
}
HTML完整代碼:
查看代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上傳</title>
<link rel="stylesheet" type="text/css" href="webuploader/webuploader.css">
<script type="text/javascript" src="common/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="webuploader/webuploader.js"></script>
<script type="text/javascript" src="common/js/include.js"></script>
<script type="text/javascript" src="upload.js"></script>
</head>
<body>
<h1>歡迎來到Marydon的博客園</h1>
<table border="0" cellpadding="0" cellspacing="0" style="width: 100%; height: 90%">
<tr>
<td style="text-align: center">
<div id="filePicker" style="line-height:15px;" class="webuploader-container">
<div class="webuploader-pick">選擇文件</div>
</div>
</td>
<td>
<span style="color: #898989">備注:掃描文件格式為xls</span>
</td>
</tr>
</table>
</body>
</html>
注意:關鍵點在於,引入文件的路徑問題,不要搞錯了。
JAVASCRIPT完整代碼:
查看代碼
var upload = new Upload();
window.onload = function(){
// 不要忘了調用WebUploader的初始化函數
upload.init();
}
function Upload() {
var object = this;
/**
* 該插件批量上傳的本質:
* 每個文件單獨發送一個上傳請求,所以,其本質還是單文件上傳,而不是真正含義上的:一次請求包含多個文件。
*/
this.init = function () {
var uploadUrl = baseUrl + '/file/upload.do';
var uploader = WebUploader.create({
auto: true, // 選擇文件后自動上傳,默認不自動上傳需要觸發
swf: baseUrl + '/webuploader/Uploader.swf', // swf文件路徑
server: uploadUrl, // 上傳文件的接口(替換成你們后端給的接口路徑)
// 選擇文件的按鈕。可選。
// 內部根據當前運行是創建,可能是input元素,也可能是flash.
pick: '#filePicker',
accept: {
//extensions: 'xls,xlsx', // 允許的文件后綴,不帶點,多個用逗號分割,這里支持老版的Excel和新版的
mimeTypes: 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
// 說明:extensions沒用,不生效
// 只有mimeTypes的值對應文件域的accept屬性,僅能通過它來控制上傳文件的類型
// 文件類型,也可以通過“.后綴名”的方式來實現,如:.xls,.xlsx
},
resize: false, // 不壓縮image, 默認如果是jpeg,文件上傳前會壓縮一把再上傳!
/* duplicate :true */ //可重復上傳
});
/**
* 文件被加入隊列之前觸發
* @description 主要用來傳其它參數
* @explain 我沒有在這里演示,沒有傳其它參數
*/
uploader.on('beforeFileQueued', function (file) {
var obj = new Object();
obj.THEMEID = $('#THEMEID').val();
uploader.options.formData = obj;
});
/**
* 上傳成功
*/
uploader.on('uploadSuccess', function( file, result) {
alert(JSON.stringify(result));
});
/**
* 上傳失敗
*/
uploader.on('uploadError', function( file, result) {
// alert('文件上傳失敗!');
alert(JSON.stringify(result));
});
};
}
注意:關鍵點在於,需要對插件WebUploader進行初始化。
3.后端代碼
難點在於:如何接收前端發過來的請求?
其實,很簡單:參數file的值就是MultipartFile類型的文件。
文件上傳,上傳的參數至少會有以下6個參數:
為了方便對於參數的使用,我用實體類來接收這些參數,這樣,在后續用到的時候就會很方便。
查看代碼
import lombok.Getter;
import lombok.Setter;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 文件上傳請求參數
* @description:
* @author: Marydon
* @date: 2022-01-10 15:38
* @version: 1.0
* @email: marydon20170307@163.com
*/
@Getter
@Setter
public class UploadParamsDto {
@NotBlank(message = "id:不能為空")
private String id;
@NotBlank(message = "name:不能為空")
private String name;
@NotBlank(message = "type:不能為空")
private String type;
@NotBlank(message = "lastModifiedDate:不能為空")
private String lastModifiedDate;
@NotBlank(message = "size:不能為空")
private String size;
@NotNull(message = "file:不能為空")
private MultipartFile file;
}
如果不需要對參數進行非空校驗,刪掉這些屬性對應的注解即可。
JAVA控制層完成代碼:
查看代碼
import com.example.upload.web.dto.UploadParamsDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
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.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 文件上傳控制層
* @description:
* @author: Marydon
* @date: 2022-01-10 10:16
* @version: 1.0
* @email: marydon20170307@163.com
*/
@Slf4j
@RestController
@RequestMapping("/file")
public class UploadController {
// 文件存放路徑:項目路徑/files
private static final String UPLOADED_FOLDER = System.getProperty("user.dir") + File.separator + "files";
/*
* 文件上傳
* @attention: 不支持批量上傳
* @description: 請求類型-multipart/form-data
* @date: 2022/1/11 10:23
* @param: paramsDto
* @return: java.util.Map
*/
@PostMapping("/upload.do")
public Map<String, Object> upload(@Validated UploadParamsDto paramsDto) {
// 即將返回的數據
Map<String, Object> resultMap = new HashMap<>(2);
resultMap.put("isSuccess", true);
resultMap.put("error", "");
Map<String, String> errorMap = new HashMap<>();
// 獲取上傳的文件
MultipartFile file = paramsDto.getFile();
// 新文件的文件名
String fileName = System.currentTimeMillis() + "_" + paramsDto.getName();
// 新文件的生成
File copyFile = new File(UPLOADED_FOLDER + File.separator + fileName);
// 目錄不存在就自動創建
copyFile.mkdirs();
try {
// 將multipartfile文件轉移到file對象所代表的文件中
// 該方法可以將文件夾直接變為文件
// 當copyFile代表的是一個目錄時,正常情況文件是無法輸入的,而transferTo()方法會將該目錄直接變為文件並輸入數據
file.transferTo(copyFile);
} catch (IOException e) {
e.printStackTrace();
log.error(e.getMessage());
// 如果報錯,意味着該文件上傳失敗,將原文件名和報錯信息返回
errorMap.put(paramsDto.getName(), e.getMessage());
}
if (!errorMap.isEmpty()) {
resultMap.put("error", errorMap);
}
return resultMap;
}
}
難點還有:如何獲取保存文件的目錄;
新文件名的命名規則;
文件的快速復制。
當然,如果只是根據上傳的文件進行解析處理,而不保存的話,不會遇到以上3個問題。
說明:
請求格式:multipart/form-data;
返回格式:application/json。
另外,如果,需要對實體類UploadParamsDto進行參數校驗,除了需要在它前面加注解@Validated外,還要引入對應的注解jar依賴。
<!--spring對參數進行校驗:hibernate validator-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.18.Final</version>
</dependency>
更多細節,我在之前的文章講過。
4.效果展示
直接來到歡迎頁
點擊“選擇文件”,選擇要上傳的文件,支持多選。
最終上傳的文件存放位置
5.擴展
通過配置文件限制上傳文件大小
如果,對上傳文件的大小有限制的話,可以通過配置來實現。
查看代碼
####spring配置####
####開發環境數據源配置
spring:
###文件上傳大小限制
servlet:
multipart:
#是否支持批量上傳(默認值 true)
enabled: true
#上傳文件最大為 1M(默認值 1M)
max-file-size: 10MB
#上傳請求最大為 10M(默認值 10M)
max-request-size: 10MB
#文件大小閾值,當大於這個閾值時將寫入到磁盤,否則存在內存中(默認值0,即:直接將文件寫入磁盤)
file-size-threshold: 0
#判斷是否要延遲解析文件(默認值false)
resolve-lazily: false
#存放上傳文件的臨時目錄
location:
禁止多選
WebUploader插件,默認支持多文件上傳;
如果我們需要限制每次只允許上傳一個文件的話,可以通過如下配置進行禁止。
pick對象里面聲明multiple屬性,並將值設為false。
pick: {
'id': '#filePicker',//文件上傳按鈕所在位置(通過ID來指定)
'multiple': false //禁止多選
},
頁面初始化后,文本域沒有multiple屬性。
下面這種方式,會默認允許多選。
說明:
文件域的multiple屬性,可以控制文件的單選和多選;
支持多選:聲明multiple屬性;
禁止多選:不聲明multiple屬性。
<input type="file" multiple="multiple">
文件域的accept屬性,可以控制上傳文件的文件類型。
方式一:
<input type="file" accept="文件對應的文件類型(mimetype),如:application/pdf">
方式二:推薦使用
<input type="file" accept="文件對應的文件后綴名(.文件名),如:.sql">
2023年3月27日15:31:48
上傳jpg文件
當使用該插件上傳jpg格式圖片時,可能會上傳失敗,報錯信息如下:
需要聲明compress屬性,如果不需要壓縮的話,將值設為false即可。
var uploader = WebUploader.create({
auto: true,
swf: baseUrl + '/commons/js/ueditor/third-party/webuploader/Uploader.swf',
server: upUrl,
pick: '#filePicker',
method: 'POST',
resize: false,
compress: false,// 不聲明將導致部分jpg文件無法上傳
accept: {
title: 'Images',
extensions: 'gif,jpg,jpeg,bmp,png,pdf',
mimeTypes: 'image/*,application/pdf'
}
});
寫在最后
哪位大佬如若發現文章存在紕漏之處或需要補充更多內容,歡迎留言!!!