excel文件上傳下載問題總結

最近做項目跟excel打交道比較多,很多業務需求涉及到excel文件的上傳、解析和下載,在這個過程中,我們難免會遇到一些棘手的問題。下面我將對這些問題做一個描述,提供出我的解決方案以及思考和總結。

一、斜線的問題

考慮到要用poi來開發如下帶有斜線的表格,我之前也沒遇過這樣的需求,在網上也苦苦找尋了許久,一直沒答案。後來突然來了靈感,爲何不用讀取模板的方式呢,我們可以直接在模板上把這個斜線給畫好。一般情況下,斜線上下的文本也很少變動,於是我們也可以把“城市”和“手機品牌”一起畫在模板上。
在這裏插入圖片描述

二、RestTemplate上傳文件中文名亂碼問題

一般情況下,我們用RestTemplate上傳中文名的文件,用以下代碼不會有文件名亂碼問題。但是如果你有了spring-web5.0以下的jar包,你就會很鬱悶,不管我們怎麼設置編碼格式,接收端收到的文件名都會亂碼。

	@Test
    public void restTemplateTransferFile(){
        final String filePath = "F:";
        final String fileName = "我的文件.txt";
        final String url = "http://localhost:8080/file/upload";

        RestTemplate restTemplate = new RestTemplate();

        //設置請求頭
        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("multipart/form-data");
        headers.setContentType(type);

        //設置請求體,注意是LinkedMultiValueMap
        FileSystemResource fileSystemResource = new FileSystemResource(filePath+"/"+fileName);
        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
        form.add("file", fileSystemResource);
        form.add("filename",fileName);

        //用HttpEntity封裝整個請求報文
        HttpEntity<MultiValueMap<String, Object>> files = new HttpEntity<>(form, headers);

        String s = restTemplate.postForObject(url, files, String.class);
        System.out.println(s);
    }

這個問題阻礙了我很長時間,開始網上也找了幾個解決方案,有些一看篇幅很長,我就覺得不就是編碼格式問題嘛,幹嘛要長篇大論一番,直接找到編碼的位置設置下就好了。但是但是,這次還真不行了,在哪個地方設置編碼,設置任何編碼都不行。後來我也是debug進去找到了http表單的轉換類,看到了以下代碼。注意紅色框框這裏,編碼竟然寫死是ASCII碼,我的媽呀,問題就在這裏。學過編碼的同學都知道,ASCII碼就只有128個基本字符,根本編不了中文。
在這裏插入圖片描述

定位到問題後,我們就好辦了。結合網上的搜索,得出兩種解決方案。
第一是升級spring-web到5.0以上(我也是服了,spring竟然要到5.0以上才解決)。5.0以上我們看到沒有硬編碼

	 private byte[] getBytes(String name) {
         return name.getBytes(this.charset);
     }

第二是自定義一個表單的轉換類,把spring-web默認的給替換了。由於我們項目是有統一的架構版本,不能私自升級spring-web包,所以最終只能用第二種方案。具體的方法如下:

  1. 複製FormHttpMessageConverter類下的所有代碼,新建UploadFileFormHttpMessageConverter類
public class UploadFileUploadFileFormHttpMessageConverter implements HttpMessageConverter<MultiValueMap<String, ?>> {
	// 省略
}
  1. 修改getAsciiBytes方法,編碼改爲UTF-8
		private byte[] getAsciiBytes(String name) {
            try {
                return name.getBytes(StandardCharsets.UTF_8.name());
            } catch (UnsupportedEncodingException var3) {
                throw new IllegalStateException(var3);
            }
        }

3.新建UploadRestTemplateUtil,替換新的converter

public class UploadRestTemplateUtil {
 
	/**
	 * 獲取上傳文件的restTemplate
	 * @return
	 */
	public static RestTemplate getRestTemplate() {
		RestTemplate restTemplate = new RestTemplate();
		List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
		messageConverters.add(new MappingJackson2HttpMessageConverter());
 
		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
		stringHttpMessageConverter.setWriteAcceptCharset(true);
 
		List<MediaType> mediaTypeList = new ArrayList<>();
		mediaTypeList.add(MediaType.ALL);
 
		for (int i = 0; i < messageConverters.size(); i++) {
			HttpMessageConverter<?> converter = messageConverters.get(i);
			if (converter instanceof StringHttpMessageConverter) {
				messageConverters.remove(i);
				messageConverters.add(i, stringHttpMessageConverter);
			}
			if (converter instanceof MappingJackson2HttpMessageConverter) {
				try {
					((MappingJackson2HttpMessageConverter) converter).setSupportedMediaTypes(mediaTypeList);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			if (converter instanceof FormHttpMessageConverter) {
				// 針對文件上傳文件名亂碼情況使用自定義的converter
				UploadFileFormHttpMessageConverter myConverter = new UploadFileFormHttpMessageConverter();
				myConverter.setCharset(StandardCharsets.UTF_8);
 
				messageConverters.remove(i);
				messageConverters.add(i, myConverter);
			}
		}
		return restTemplate;
	}
}

三、導出文件OOM問題

POI生成文件佔用大內存的問題,相信很多開發同學都知道,如果在高併發場景下,容易遇到OOM問題。當時我們在開發環境壓測下載excel的時候發現,服務很快就拋出了OOM,我們很快定位到是poi佔用內存太大了。這個地方是我們代碼疏忽了,漏加併發線程控制。當初我們設計文件下載的時候就有用@Async 註解做文件異步的處理。

後來我們也做了避免OOM這方面的討論,得出結論是有兩個方案,就看併發程度如何。

第一,文件下載併發低,我們直接做成異步調用,用@Async做併發控制,前端輪詢文件生成結果。

第二,文件下載併發高,我們引進阿里的EasyExcel工具。這個工具是一行一行讀取excel,特別省內存,我們在後面的開發中首先都是考慮採用這個工具生成excel。

關於EasyExcel的使用方法,可以查看官網。對於簡單規整的excel,我們首先考慮使用這個工具。還有它的填充功能,我覺得還是特別讚的。但是對於一些單元格格式比較複雜的excel,我看EasyExcel沒有很好的兼容支持,後面再持續關注這工具的發展。

四、輸入輸出流的實現

開發中有很多這樣的需求:字節數組生成文件流,文件流生成文件,字符串寫到文件等等。這都是輸入輸出流api的事情,好久沒有接觸過這方面的開發了,感覺都忘了大部分,後面專門寫一篇文章來溫習溫習。

參考:RestTemplate上傳文件中文名亂碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章