springboot上傳文件和下載文件
上傳文件
在這里我們主要有這幾個改變:
- 使用按鈕上傳
- 放棄form表單上傳,使用js上傳文件。
使用按鈕上傳
實現效果:
點擊上傳模板按鈕,彈出文件框:
剛開始實在是想偷懶,直接百度,結果出來都是一大堆比較麻煩的。后來就自己手動調了。但是提供的思路確實值得借鑒,大概的思路都是:通過將輸入框的位置設置可移動的,然后設置透明度為0即可。代碼如下:
<button type="button" th:onclick="batchDelete()" class="btn btn-file btn-sm">
上傳模板
</button>
<input class="file" type="file" name="file" onchange="uploadTemplateExcel()"
style="position: absolute;margin-top: -33px;opacity: 0;margin-left: 219px;width: 70px">
這樣我們就可以使用按鈕進行上傳文件了。
使用js上傳
js代碼:
function uploadTemplateExcel(){
let formData=new FormData();
formData.append("file",$(".file")[0].files[0]);
$.ajax({
type:"POST",
url:"/xxxx/file/xxxx/upload",
data:formData,
cache:false,
contentType:false,
processData:false,
dataType:"json",
success:function (response) {
console.log(response);
if(response !== 0){
toastr.success("上傳成功","提示:");
setTimeout(function () {
window.location.reload();
},2000)
}else{
toastr.error("上傳失敗,請重試","提示:");
}
}
})
}
后端代碼
先說明下我們整體的大概的思路:
因為我這里只能有一個這個文件,所以處理的時候,可以重復保存,然后在保存的時候,先刪除所有之前保存過的文件,然后再保存新的。
上傳的時候,上傳文件到服務器,然后再存到數據庫里面。
后台代碼已經通過工具類進行封裝好了。現在po出:
FileUploadUtils類:
package com.linkai.utils.file;
import com.linkai.config.Global;
import com.linkai.constant.Constants;
import com.linkai.exception.file.FileNameLengthLimitExceededException;
import com.linkai.exception.file.FileSizeLimitExceededException;
import com.linkai.exception.file.InvalidExtensionException;
import com.linkai.utils.DateUtils;
import com.linkai.utils.uuid.IdUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 文件上傳工具類
*
* @author ruoyi
*/
@Component
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 = Global.getProfile();
public static void setDefaultBaseDir(String defaultBaseDir)
{
FileUploadUtils.defaultBaseDir = defaultBaseDir;
}
public static String getDefaultBaseDir()
{
return defaultBaseDir;
}
/**
* 以默認配置進行文件上傳
*
* @param file 上傳的文件
* @return 文件名稱
* @throws Exception
*/
public static Map<String, 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 Map<String, 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 allowedExtension 上傳文件類型
* @return 返回上傳成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws FileNameLengthLimitExceededException 文件名太長
* @throws IOException 比如讀寫文件出錯時
* @throws InvalidExtensionException 文件校驗異常
*/
public static Map<String,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);
Map<String, String> map = new HashMap<>();
map.put("fileName",fileName);
map.put("pathFileName",desc.getAbsolutePath());
return map;
}
/**
* 編碼文件名
*/
public static final String extractFilename(MultipartFile file)
{
String fileName = file.getOriginalFilename();
String extension = getExtension(file);
fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + 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 = Global.getProfile().length() + 1;
String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
return pathFileName;
}
/**
* 文件大小校驗
*
* @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;
}
}
現在po出后端處理代碼:
/**
* 基本資料中的商品的模板上傳
*
* @param file
* @return
*/
@RequestMapping(value = "/xxxx/upload", method = RequestMethod.POST)
@ResponseBody
public int uploadBackProductInfoUploader(@RequestParam(value = "file") MultipartFile file) {
try {
//找出已經存在的數據並且進行刪除。
final HashMap<String, Object> columnMap = new HashMap<>();
columnMap.put("pro_id",-1);
imgService.removeByMap(columnMap);
//保存到服務器上
Map<String, String> fileUrlInfo = FileUploadUtils.upload(file);
//保存到數據庫中,這里根據你自己定義構建。
return fileImgService.save2Db(file,-1,fileUrlInfo);
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
需要注意的是,必須加上@ResponseBody
這個注解,要不前端收不到信息。
到此,上傳文件已經完成。
下載文件
需要完成的功能
點擊下載模板按鈕,完成模板中的下載。
<button type="button" th:onclick="downloadTemplate()" class="btn btn-file btn-sm">
下載模板
</button>
js代碼
因為是用的按鈕,所以用js比較好。
function downloadTemplate(){
location.href='/admin/file/bproduct/download'
}
切記:這塊不能用ajax異步傳輸,包括POST、GET方法,要不后台不會返回給前台數據,直白點就是瀏覽器不會有反應,不會出現下載彈框的
后台代碼
繼承org.apache.commons.io.FileUtils
的FileUtils工具類:
package com.linkai.utils.file;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* 文件處理工具類
*
* @author ruoyi
*/
public class FileUtils extends org.apache.commons.io.FileUtils
{
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
/**
* 輸出指定文件的byte數組
*
* @param filePath 文件路徑
* @param os 輸出流
* @return
*/
public static void writeBytes(String filePath, OutputStream os) throws IOException
{
FileInputStream fis = null;
try
{
File file = new File(filePath);
if (!file.exists())
{
throw new FileNotFoundException(filePath);
}
fis = new FileInputStream(file);
byte[] b = new byte[1024];
int length;
while ((length = fis.read(b)) > 0)
{
os.write(b, 0, length);
}
}
catch (IOException e)
{
throw e;
}
finally
{
if (os != null)
{
try
{
os.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
if (fis != null)
{
try
{
fis.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
}
}
/**
* 刪除文件
*
* @param filePath 文件
* @return
*/
public static boolean deleteFile(String filePath)
{
boolean flag = false;
File file = new File(filePath);
// 路徑為文件且不為空則進行刪除
if (file.isFile() && file.exists())
{
file.delete();
flag = true;
}
return flag;
}
/**
* 文件名稱驗證
*
* @param filename 文件名稱
* @return true 正常 false 非法
*/
public static boolean isValidFilename(String filename)
{
return filename.matches(FILENAME_PATTERN);
}
/**
* 下載文件名重新編碼
*
* @param request 請求對象
* @param fileName 文件名
* @return 編碼后的文件名
*/
public static String setFileDownloadHeader(HttpServletRequest request, String fileName)
throws UnsupportedEncodingException
{
final String agent = request.getHeader("USER-AGENT");
String filename = fileName;
if (agent.contains("MSIE"))
{
// IE瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
}
else if (agent.contains("Firefox"))
{
// 火狐瀏覽器
filename = new String(fileName.getBytes(), "ISO8859-1");
}
else if (agent.contains("Chrome"))
{
// google瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
}
else
{
// 其它瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
/**
* 下載文件名重新編碼
*
* @param response 響應對象
* @param realFileName 真實文件名
* @return
*/
public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException
{
String percentEncodedFileName = percentEncode(realFileName);
StringBuilder contentDispositionValue = new StringBuilder();
contentDispositionValue.append("attachment; filename=")
.append(percentEncodedFileName)
.append(";")
.append("filename*=")
.append("utf-8''")
.append(percentEncodedFileName);
response.setHeader("Content-disposition", contentDispositionValue.toString());
}
/**
* 百分號編碼工具方法
*
* @param s 需要百分號編碼的字符串
* @return 百分號編碼后的字符串
*/
public static String percentEncode(String s) throws UnsupportedEncodingException
{
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
return encode.replaceAll("\\+", "%20");
}
/**
* 設置文件下載頭
* @param response response
* @param fileName 文件名
*/
public static void setFileResponseContent(HttpServletResponse response,String fileName){
response.setHeader("content-type", "image/png");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
}
}
這幾個方法其實也比較常用的,使用的話,直接類名.就行,不用注解到spring容器中。
controller
/**
* 下載一個樣例
* @param request 請求
* @param response 響應
* @return 異常
* @throws Exception 全局異常
*/
@RequestMapping(value = "/bproduct/download",method = RequestMethod.GET)
@ResponseBody
public String exportSelectedSimpleCases(HttpServletRequest request, HttpServletResponse response) throws Exception{
String filePath= null;
try {
request.setCharacterEncoding("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
filePath = fileImgService.downloadTemplate(request,response);
return filePath;
}
service
/**
* 下載一個樣例
* @param request request
* @param response response
* @return
*/
String downloadTemplate(HttpServletRequest request, HttpServletResponse response);
serviceImpl
/**
* 下載一個樣例
* @param request request
* @param response response
* @return
*/
@Override
public String downloadTemplate(HttpServletRequest request, HttpServletResponse response) {
//獲取文件名
final QueryWrapper<Img> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("pro_id",-1);
final Img one = imgService.getOne(queryWrapper);
OutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
if (FileUtils.isValidFilename(one.getImgTitle())) {
FileUtils.setFileResponseContent(response,one.getImgTitle());
try {
FileUtils.writeBytes(one.getImgLocalUrl(),outputStream);
} catch (IOException e) {
e.printStackTrace();
}
}else{
System.out.println("下載失敗,文件名檢驗失敗!");
return null;
}
return one.getImgLocalUrl();
}
至此,下載文件也結束了。
需要注意的也是,必須加上@ResponseBody
這個注解,要不不會彈出彈框。