前端发送文件下载请求,接收后端发送的csv文件,关于乱码问题的处理

前端发送文件下载请求,接收后端发送的csv文件,关于乱码问题的处理

成功界面展示,关于乱码,测试了很多别人的编码方案,没能实现效果,这里实际应用如下。

在这里插入图片描述

1、前端发送导出文件请求

 downLoadFile(){
    
    
      this.$http.get('/export').then(res => {
    
    
        //获取文件名
        /**
         * Content-Disposition此处必须开头小写,大写不通过。
         * */
        var disposition = res.headers["content-disposition"];
        var fileName = decode(disposition.substring(disposition.indexOf("=") + 1), "UTF-8");
        console.log(fileName)
        //创建一个a标签并设置href属性,之后模拟人为点击下载文件
        let link = document.createElement('a');
        /**
         * new Blob(['\uFEFF' + res.data]
         * 此处是重点,解决csv下载的文件用Excel打开乱码,其他打开不乱码的问题
         * */
        link.href = window.URL.createObjectURL(new Blob(['\uFEFF' + res.data]))
        link.download = fileName;//设置下载文件名
        link.click();//模拟点击
        //释放资源并删除创建的a标签
        URL.revokeObjectURL(link.href);
        /**
         * 这里做判断,因为第一次点击存在link删除,再点击就会报错。不是ChildNode
         * */
        if (document.body.contains(link)) {
    
    
          document.body.removeChild(link);
        }

      }, (response) => {
    
    
        console.error(response)
      });
    },

2、后端下载并导出Csv文件

    /**
     * 下载并导出csv文件
     * */
    @RequestMapping(value = "/export")
    public void getSkuList1(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
        //获取到批量识别的短链接数据结果
        LinkedHashSet<ShortLinks> linkedHashSet = ParseUrl.getLinkedHashSet();
        List<Object[]> cellList = new ArrayList<>();

        if (null == linkedHashSet || linkedHashSet.isEmpty()){
    
    
            System.out.println("导出数据中没有处理的数据!");
        }else {
    
    
            for (ShortLinks sl : linkedHashSet) {
    
    
                Object[] object = {
    
    sl.getCreate_time(), sl.getShort_link(), sl.getDomain_name(), sl.getIp_address(), sl.getIp_territorial(),
                        sl.getRecord_status(), sl.getDomain_type()};
                cellList.add(object);
            }

            String[] tableHeaderArr = {
    
    "录入时间","短链接名", "对应域名", "对应IP", "IP归属地", "备案状态", "域名状态", "域名类型"};
            String fileName = "导出文件.csv";
            byte[] bytes = ExportCSVUtil.writeCsvAfterToBytes(tableHeaderArr,  cellList);
            ExportCSVUtil.responseSetProperties(fileName, bytes, request, response);
        }
    }

3、下载文件的工具类(这里编码问题做了处理)

package com.example.util;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.io.ByteOrderMark;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *  写csv文件
 * @Author lizian
 * @Date 2020-07-26
 */
public class ExportCSVUtil {
    
    

    private static final Logger logger = LoggerFactory.getLogger(ExportCSVUtil.class);

    /**
     * 写CSV并转换为字节流
     * @param tableHeaderArr 表头
     * @param cellList 数据
     * @return
     */
    public static byte[] writeDataAfterToBytes(String[] tableHeaderArr, List<String> cellList) {
    
    
        byte[] bytes = new byte[0];
        ByteArrayOutputStream byteArrayOutputStream = null;
        OutputStreamWriter outputStreamWriter = null;
        BufferedWriter bufferedWriter = null;
        try {
    
    
            byteArrayOutputStream = new ByteArrayOutputStream();
            outputStreamWriter = new OutputStreamWriter(byteArrayOutputStream,StandardCharsets.UTF_8);
            bufferedWriter = new BufferedWriter(outputStreamWriter);
            //excel文件需要通过文件头的bom来识别编码,而CSV文件格式不自带bom,所以写文件时,需要先写入bom头,否则excel打开乱码
            bufferedWriter.write(new String(ByteOrderMark.UTF_8.getBytes()));
            //写表头
            StringBuilder sb = new StringBuilder();
            String tableHeader = String.join(",", tableHeaderArr);
            sb.append(tableHeader + StringUtils.CR + StringUtils.LF);
            for (String rowCell : cellList) {
    
    
                sb.append(rowCell + StringUtils.CR + StringUtils.LF);
            }
            bufferedWriter.write(sb.toString());
            bufferedWriter.flush();
            //把输出流转换字节流
            bytes = byteArrayOutputStream.toString(StandardCharsets.UTF_8.name()).getBytes();
            return bytes;
        } catch (IOException e) {
    
    
            logger.error("writeDataAfterToBytes IOException:{}", e.getMessage(), e);
        } finally {
    
    
            try {
    
    
                if (bufferedWriter != null) {
    
    
                    bufferedWriter.close();
                }
                if (outputStreamWriter != null) {
    
    
                    outputStreamWriter.close();
                }
                if (byteArrayOutputStream != null) {
    
    
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
    
    
                logger.error("iostream close IOException:{}", e.getMessage(), e);
            }
        }
        return bytes;
    }

    /**
     * 写CSV并转换为字节流
     * @param headers 表头
     * @param cellList 表数据
     * @return
     */
    public static byte[] writeCsvAfterToBytes(String[] headers,  List<Object[]>  cellList) {
    
    
        byte[] bytes = new byte[0];
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(byteArrayOutputStream, StandardCharsets.UTF_8);
        BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
        CSVPrinter  csvPrinter = null;
        try {
    
    
            //创建csvPrinter并设置表格头
            csvPrinter = new CSVPrinter(bufferedWriter, CSVFormat.DEFAULT.withHeader(headers));
            //写数据
            csvPrinter.printRecords(cellList);
            csvPrinter.flush();
            bytes = byteArrayOutputStream.toString(StandardCharsets.UTF_8.name()).getBytes();
        } catch (IOException e) {
    
    
            logger.error("writeCsv IOException:{}", e.getMessage(), e);
        } finally {
    
    
            try {
    
    
                if (csvPrinter != null) {
    
    
                    csvPrinter.close();
                }
                if (bufferedWriter != null) {
    
    
                    bufferedWriter.close();
                }
                if (outputStreamWriter != null) {
    
    
                    outputStreamWriter.close();
                }
                if (byteArrayOutputStream != null) {
    
    
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
    
    
                logger.error("iostream close IOException:{}", e.getMessage(), e);
            }
        }
        return bytes;
    }

    /**
     * 设置下载响应
     * @param fileName
     * @param bytes
     * @param response
     */
    public static void responseSetProperties(String fileName, byte[] bytes, HttpServletRequest request, HttpServletResponse response) {
    
    
        try {
    
    
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            response.setHeader("Pragma", "public");
            response.setHeader("Cache-Control", "max-age=30");

            /**
             * FILENAME为我们要下载的文件的文件名(包含后缀),
             * RFC 2183规定FILENAME只能为US-ASCII码,然而现代浏览器中许多已经支持UTF-8编码了,
             * 但各个浏览器的支持规则不同。
             * 在IE、chrome中,可以直接用FILENAME作为下载文件的名称,但是Firefox却不支持这样。我们直接上代码吧。
             * */

            /**
             * 2022/11/27   2:05分解决困扰了一天多的response.setHeader(fileName)中文文件名编码问题
             * */
            fileName = new String(fileName.getBytes(), "ISO-8859-1");
            response.setHeader("Content-Disposition","attachment;filename=" + fileName);
            
            //Content-Disposition字段暴露给前端,才能够进行给下载的文件命名,如果是浏览器直接访问后端接口就不用暴露。
            response.setHeader("Access-Control-Expose-Headers","Content-Disposition");
            response.setHeader("content-Type", "application/csv;charset=utf-8");

            // 响应类型,编码
            OutputStream outputStream = response.getOutputStream();
            // 直接访问接口,在文件的开头,在写入任何内容之前,向流中写入0xEF,0xBB,0xBF三个字节(此处用16进制表示), 用以告诉Excel,此CSV文件的编码为UTF-8.
            /**
             * 但是当文件被前端vue接收时,还是要做处理,不然还是乱码。
             *
             *  *前端处理:
             *  * new Blob(['\uFEFF' + res.data]
             *  * 此处是重点,解决csv下载的文件用Excel打开乱码,其他打开不乱码的问题
             *  * link.href = window.URL.createObjectURL(new Blob(['\uFEFF' + res.data]))
             * */
            outputStream.write(new byte[]{
    
    (byte) 0xEF, (byte) 0xBB,(byte) 0xBF});
            outputStream.write(bytes);
            outputStream.flush();
        } catch (IOException e) {
    
    
            logger.error("isstream error:{}", e.getMessage(), e);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/m0_45057216/article/details/128060059
今日推荐