SpringMVC 下载文本实现边转码边下载
                    
                    
                
            边转码边下载
下载通常都会遇到这种情况,代码里通常保存为 utf-8 格式,但是用户想要 gbk 格式,实现思路如下:
方案一: PrintWriter + CharacterEncoding
SpringMVC 框架的接口中 Response 有 ServletOutputStream 和 PrintWriter 两种输出,还有一个 setCharacterEncoding 方法。
OutPutStream 视为写入二进制数据,容器不对二进制数据进行编码,即 setCharacterEncoding 无效。所以先采用 PrintWriter 搭配 setCharacterEncoding 方法。
@GetMapping("/downloadFile1")
public void downloadFile1(HttpServletResponse response) throws IOException{
    //读取本地文件
    File file = new File(LOCAL_FILE_PATH);
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
    //设置下载
    response.setHeader("Content-Disposition", "attachment;filename=file.csv");
    response.setContentType("application/octet-stream");
    response.setCharacterEncoding("GB2312");
    //传输
    BufferedWriter bufferedWriter = new BufferedWriter(response.getWriter());
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        bufferedWriter.write(line + "\r\n");
        bufferedWriter.flush();
    }
    bufferedReader.close();
    bufferedWriter.close();
}
无用,没有仔细研究,应该 setCharacterEncoding 只是告诉浏览器是什么编码,没有解码功能。
方案二:ServletOutputStream + OutputStreamWriter
将 response.getOutputStream() 获取到的流用 OutputStreamWriter 包装。结果可行。
@GetMapping("/downloadFile2")
public void downloadFile2(HttpServletResponse response) throws IOException{
    //读取本地文件
    File file = new File(LOCAL_FILE_PATH);
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
    //设置下载
    response.setHeader("Content-Disposition", "attachment;filename=file.csv");
    response.setContentType("application/octet-stream");
    //传输
    OutputStreamWriter writerStream = new OutputStreamWriter(response.getOutputStream(),"GBK");
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        writerStream.write(line + "\r\n");
        writerStream.flush();
    }
    bufferedReader.close();
    writerStream.close();
}
方案三:ServletOutputStream + getBytes
在按行写入字符串时,使用 getBytes 方法将字符串转换为想要输出的编码格式。结果可行。
@GetMapping("/downloadFile3")
public void downloadFile3(HttpServletResponse response) throws IOException{
    //读取本地文件
    File file = new File(LOCAL_FILE_PATH);
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
    //设置下载
    response.setHeader("Content-Disposition", "attachment;filename=file.csv");
    response.setContentType("application/octet-stream");
    //传输
    OutputStream outputStream = response.getOutputStream();
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        outputStream.write((line + "\r\n").getBytes("GB2312"));
        outputStream.flush();
    }
    bufferedReader.close();
    outputStream.close();
}
无需转换的写法
若不需要转码的话,就很简短了,用字节流就行了。
@GetMapping("/downloadFile4")
public void downloadFile4(HttpServletResponse response) throws IOException{
    //读取本地文件
    File file = new File(LOCAL_FILE_PATH);
    FileInputStream fis = new FileInputStream(file);
    //设置下载
    response.setHeader("Content-Disposition", "attachment;filename=file.csv");
    response.setContentType("application/octet-stream");
    //传输
    OutputStream os = response.getOutputStream();
    byte[] buffer = new byte[4096];
    int b = -1;
    while ((b = fis.read(buffer)) != -1) {
        os.write(buffer, 0, b);
    }
    fis.close();
    os.close();
}
简写的话可以用第三方包。
import org.springframework.util.FileCopyUtils;
@GetMapping("/downloadFile5")
public void downloadFile5(HttpServletResponse response) throws IOException{
    //读取本地文件
    File file = new File(LOCAL_FILE_PATH);
    InputStream inputStream= new BufferedInputStream(new FileInputStream(file));
    //设置下载
    response.setHeader("Content-Disposition", "attachment;filename=file.csv");
    response.setContentType("application/octet-stream");
    //传输
    FileCopyUtils.copy(inputStream, response.getOutputStream());
}
多文件打包下载
当需要下载多个文件时,可以将其打包成一个文件,然后下载。Java 自带的 ZipOutputStream 可以实现。
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@Override
public void download(OutputStream outputStream) throws IOException {
    // Create the ZIP file
    ZipOutputStream zipOut = new ZipOutputStream(outputStream);
    // image demo
    Map<String, String> imageMap = new HashMap<>;
    //add file to zip
    ZipEntry zipEntry;
    for (String imageName : imageMap.keySet()) {
        // http://192.168.0.102:13080/pcg/files/image/64280347-afa8-4057-8941-ccbaeebf3c4b
        File fileToZip = new File(StaticProperty.IMAGE_PATH, imageMap.get(imageName));
        try (FileInputStream fis = new FileInputStream(fileToZip)) {
            zipEntry = new ZipEntry(imageName);
            zipOut.putNextEntry(zipEntry);
            byte[] bytes = new byte[1024];
            int length;
            while((length = fis.read(bytes)) >= 0) {
                zipOut.write(bytes, 0, length);
            }
        }catch (FileNotFoundException fnfe){
            LOG.warn("file {} not found", imageMap.get(imageName));
        }
        zipOut.closeEntry();
    }
    zipOut.flush();
    zipOut.close();
补充:
- 使用 Nginx 可以不通过接口前端直接下载,后续可以尝试采用此方法。
 
Last modified on 2020-03-04