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
这个注解,要不不会弹出弹框。