springboot上传文件和下载文件

上传文件

在这里我们主要有这几个改变:

  1. 使用按钮上传
  2. 放弃form表单上传,使用js上传文件。

使用按钮上传

实现效果:




springboot 下载网络图片到本地 springboot上传下载文件_js


点击上传模板按钮,弹出文件框:


springboot 下载网络图片到本地 springboot上传下载文件_curl_02


刚开始实在是想偷懒,直接百度,结果出来都是一大堆比较麻烦的。后来就自己手动调了。但是提供的思路确实值得借鉴,大概的思路都是:通过将输入框的位置设置可移动的,然后设置透明度为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这个注解,要不前端收不到信息。

到此,上传文件已经完成。


下载文件

需要完成的功能


springboot 下载网络图片到本地 springboot上传下载文件_html_03


点击下载模板按钮,完成模板中的下载。

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