zoukankan      html  css  js  c++  java
  • Excel打开csv格式文件中文乱码的解决

       【乱码解决】

      接到一个需求,需要将PC管理端页面的Excel文件下载改为CSV的格式。据说CSV也是一种比较通用的做法,因为Excel各个版本对于单Sheet的行数都会有限制,大数据量的情况下直接用CSV会方便很多,另外如果使用Excel组装数据,那么接口实现的内存也会占用更多(更多的对象)。

      直接上代码:

    package com.jf.mzzc.manage.util.exportCSVUtil;
    
    import com.jf.mzzc.common.util.RFC5987StringUtils;
    import com.opencsv.CSVWriter;
    import org.springframework.web.servlet.view.AbstractView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.OutputStreamWriter;
    import java.nio.charset.StandardCharsets;
    import java.util.List;
    import java.util.Map;
    
    public class CsvView extends AbstractView {
        public static final String TEMPLATE_KEY = "templateName";
        public static final String OUTPUT_NAME_KEY = "output";
        public static final Integer LIMIT_RAW = 15000;
    
        /**
         * Subclasses must implement this method to actually render the view.
         * <p>The first step will be preparing the request: In the JSP case,
         * this would mean setting model objects as request attributes.
         * The second step will be the actual rendering of the view,
         * for example including the JSP via a RequestDispatcher.
         *
         * @param model    combined output Map (never {@code null}),
         *                 with dynamic values taking precedence over static attributes
         * @param request  current HTTP request
         * @param response current HTTP response
         * @throws Exception if rendering failed
         */
        @Override
        protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            List<String[]> entryList = (List<String[]>) model.get(TEMPLATE_KEY);
            String fileName = (String) model.get(OUTPUT_NAME_KEY);
            String rfc5987FileName = RFC5987StringUtils.rfc5987_encode(fileName);
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename="" + rfc5987FileName + "";filename*=utf-8''" + rfc5987FileName);
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(response.getOutputStream(),
                    StandardCharsets.UTF_8);
            CSVWriter writer = new CSVWriter(outputStreamWriter);
            for (int i = 0; i < entryList.size(); i++) {
                String[] strings = entryList.get(i);
                writer.writeNext(strings);
            }
            writer.close();
        }
    }

      在Controller层:

    @Controller
    @RequestMapping(value = "/manage/order")
    @Api(value = "API.VALUE", tags = "订单")
    public class ChargeOrderController {
    
        @DubboReference
        private ChargeOrderFacade chargeOrderFacade;
    
        /**
         * 带条件的csv下载
         *
         * @param model
         * @return
         */
        @ResponseBody
        @RequestMapping(value = "/download", method = RequestMethod.GET)
        public View downloadCsvView(HttpServletRequest request, Model model, ChargeOrderQueryVo queryVo) {
            Search search = SearchUtil.genSearch(request);
            List<String[]> dataList = chargeOrderFacade.extractCsvViewInfo(search, queryVo);
            CsvView view = new CsvView();
            String time = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
            model.addAttribute(CsvView.TEMPLATE_KEY, dataList);
            model.addAttribute(CsvView.OUTPUT_NAME_KEY, String.format("订单-%s.csv", time));
            return view;
        }
    }

       测试也能够正常下载CSV文件的,但是用Excel打开这个文件发现中文乱码,明明设置了UTF-8的怎么会乱码呢?再次尝试用记事本打开文件发现,中文是可以正常显示的。此时对文本另存为UTF8格式,再次用Excel打开,此时中文能够正常显示了。

      网上搜索一番找到了原因。csv文件前必须要加个BOM头,Excel才能正确打开文件。于是做如下修改:

    ...
            List<String[]> entryList = (List<String[]>) model.get(TEMPLATE_KEY);
            String fileName = (String) model.get(OUTPUT_NAME_KEY);
            String rfc5987FileName = RFC5987StringUtils.rfc5987_encode(fileName);
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename="" + rfc5987FileName + "";filename*=utf-8''" + rfc5987FileName);
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(response.getOutputStream(),
                    StandardCharsets.UTF_8);
            byte[] bom = {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};
            outputStreamWriter.write(new String(bom));
            CSVWriter writer = new CSVWriter(outputStreamWriter);
            for (int i = 0; i < entryList.size(); i++) {
                String[] strings = entryList.get(i);
                writer.writeNext(strings);
            }
            writer.close();
    ...

      此时下载的csv文件使用excel能够正常打开,且中文显示正常。

    ————————————————————————————————————————————————

      【BOM是什么】

      BOM的全称是Byte Order Mark,字节顺序标记。它是一种特殊的Unicode字符,作为一个魔数一样的存在,可以告诉正在读取这个文本流的程序三件事情:

      1.16位或者32位编码的情况下,文本流的字节顺序;

      2.一个比较高级别的确认信息,表明这个文本流的格式是Unicode;

      3.使用了Unicode字符编码。

      但是这个BOM信息,用不用都是可选的,有些软件支持,有些软件不支持。在Windows环境下,很多软件都是支持,甚至要求这个BOM信息的;但是UNIX环境下,如果携带了BOM信息,很多时候又会出问题。所以,这里要注意的是,到底使用这个流的软件是否支持BOM,到了Windows环境下,如果出现了乱码,要能想到这个问题点;而在Linux环境下,或者是跨平台的软件,如果出现了问题,又要排除这个点。

      有些软件对于BOM的设置是可选的,比如Intellij Idea中对于配置文件的设置,就可以选择;Notepad++中也是可以选的。是否选择,完全看我们的需求,但是使用不当也会带来上述问题。

      另外,要注意的是,Windows处理文本时,会自动加上这个BOM信息。比如,新建一个文本,另存为的时候,选择UTF-8格式,保存文件,此时可以看到文本的大小变成了3字节,这个3字节就是被加上了BOM信息。

  • 相关阅读:
    SQL Server优化(4)-聚集索引的重要性和如何选择聚集索引
    青岛开发区公交集团公交线路一览表
    WINDOWS下文件夹简介
    2008 SQL Server优化(2)-改善SQL语句
    【麦课在线教育mycourse】利用Js快速刷完新生安全教育课程
    Linux重启网卡的方法
    Trees
    计算机操作系统之设备管理
    linux目录结构及主要内容
    Linux mysql命令安装允许远程连接
  • 原文地址:https://www.cnblogs.com/bruceChan0018/p/15034708.html
Copyright © 2011-2022 走看看