下载文件时,如果文件名有中文等非ascii字符时,在不同的浏览器下有不同的表现,部分方案采用判断user-agent来对不同的浏览器做出不同的响应参数处理。
通过搜索,可使用如下的兼容办法:
方式一:
完全兼容现代化的浏览器和旧版IE
Content-Disposition: attachment; filename=%E6%B5%8B%E8%AF%95.txt; filename*=UTF-8''%E6%B5%8B%E8%AF%95.txt
方式二:
仅兼容现代化的浏览器
Content-Disposition: attachment; filename*=UTF-8''%E6%B5%8B%E8%AF%95.txt
在Spring mvc中的参考代码
import org.apache.commons.io.FileUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.*;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* 文件下载 demo
*
* @author liyong 2019-10-05 21:40
*/
@RestController
public class DownloadFileController {
/**
* 兼容旧版的IE 和现代浏览器
*
* @return
* @throws IOException
*/
@RequestMapping(value = "/download/compatible")
public ResponseEntity<byte[]> download() throws IOException {
File download = new ClassPathResource("application.properties").getFile();
HttpHeaders headers = new HttpHeaders();//http头信息
String fileName = "测试.txt";
String encodeFileName = URLEncoder.encode(fileName, "UTF-8");
StringBuilder contentDispositionHeaderValue = new StringBuilder();
contentDispositionHeaderValue.append("attachment; ").append("filename=").append(encodeFileName).append("; ")
.append("filename*=").append(encodeHeaderFieldParam(fileName, UTF_8));
headers.add(HttpHeaders.CONTENT_DISPOSITION, contentDispositionHeaderValue.toString());
return new ResponseEntity<>(FileUtils.readFileToByteArray(download), headers, HttpStatus.OK);
}
/**
* 仅对现代浏览器兼容,如 IE11 Edge Chrome Firefox
* IE10 没测试过
*
* @return
* @throws IOException
*/
@RequestMapping(value = "/download/new/standard")
public ResponseEntity<byte[]> downloadNewStandard() throws IOException {
File download = new ClassPathResource("application.properties").getFile();
HttpHeaders headers = new HttpHeaders();//http头信息
String fileName = "测试.txt";
ContentDisposition contentDisposition = ContentDisposition.builder("attachment").filename(fileName, UTF_8).build();
headers.setContentDisposition(contentDisposition);
return new ResponseEntity<>(FileUtils.readFileToByteArray(download), headers, HttpStatus.OK);
}
private StringBuilder encodeHeaderFieldParam(String input, Charset charset) {
Assert.notNull(input, "Input String should not be null");
Assert.notNull(charset, "Charset should not be null");
if (StandardCharsets.US_ASCII.equals(charset)) {
return new StringBuilder(input);
}
Assert.isTrue(UTF_8.equals(charset) || ISO_8859_1.equals(charset),
"Charset should be UTF-8 or ISO-8859-1");
byte[] source = input.getBytes(charset);
int len = source.length;
StringBuilder sb = new StringBuilder(len << 1);
sb.append(charset.name());
sb.append("''");
for (byte b : source) {
if (isRFC5987AttrChar(b)) {
sb.append((char) b);
} else {
sb.append('%');
char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
sb.append(hex1);
sb.append(hex2);
}
}
return sb;
}
private static boolean isRFC5987AttrChar(byte c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
c == '!' || c == '#' || c == '$' || c == '&' || c == '+' || c == '-' ||
c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
}
}
标准
https://tools.ietf.org/html/rfc2183 描述的Content-Disposition header 规范
https://tools.ietf.org/html/rfc5987 描述的是HTTP header的编码规范,因为header的value默认只能是ISO-8859-1
参考资料
https://blog.csdn.net/liuyaqi1993/article/details/78275396
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition