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信息。

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM