前言
交易单关联合同订单,合同附件打包压缩,在运营端反显附件名称,运营端人员下载查看附件 感觉很简单,附件上传下载,入库
事故经过
附件在运营端显示乱码
排查过程
一开始以为前段代码有问题,可能对应的## Content-Type类型不对,后来发现没问题,那就只能是后端代码问题啦,就开始撸底层代码
# 如何设置Content-Type(MIME)?
阿里云文档地址
文件扩展名 | Content-Type(Mime-Type) | 文件扩展名 | Content-Type(Mime-Type) |
---|
.tar | application/x-tar | .zip | application/zip |
---|
相关代码
private B2bIncomeChannelAnnex getChannelAnnex(ChannelAnnexAuditDTO dto,
List<B2bDocumentDTO> documents,
String fileName,
String annexType) throws Exception {
if (CollectionUtils.isEmpty(documents)) {
return null;
}
Date date = Date.from(LocalDate.now().atTime(LocalTime.now()).atZone(ZoneId.systemDefault()).toInstant());
if (documents.size() == 1) {
B2bIncomeChannelAnnex contractAnnex = new B2bIncomeChannelAnnex();
B2bDocumentDTO contractDocument = documents.get(0);
contractAnnex.setIncomeId(dto.getCollectOrderNo());
contractAnnex.setAnnexName(contractDocument.getDocumentName());
contractAnnex.setAnnexType(annexType);
contractAnnex.setAnnexOssUrl(contractDocument.getDocumentPath());
contractAnnex.setCreater(CollectServiceConstants.COLLECT_SYSTEM_1);
contractAnnex.setCreatedDate(date);
return contractAnnex;
}
if (documents.size() > 1) {
B2bIncomeChannelAnnex contractAnnex = new B2bIncomeChannelAnnex();
List<InputStream> files = new ArrayList<>();
List<String> fileNames = new ArrayList<>();
for (B2bDocumentDTO documentDTO : documents) {
String documentName = documentDTO.getDocumentName();
if (CollectionUtils.isNotEmpty(fileNames)) {
for (String str : fileNames) {
if (str.equals(documentName)) {
documentName = UUIDUtils.getUUID() + "-" + documentName;
break;
}
}
}
fileNames.add(documentName);
InputStream inputStream = ossSdkUtil.downLoad(documentDTO.getDocumentPath());
files.add(inputStream);
}
InputStream inputStream = OssUploadZipUtils.zipInputStreams(fileNames.toArray(new String[fileNames.size()]), files.toArray(new InputStream[files.size()]));
OssResMessage ossResMessage = ossSdkUtil.uploadDocumentStream(inputStream, URLEncoder.encode(fileName, "UTF-8") + ".zip");
log.info("压缩附件:ossResMessage:{}", JsonUtil.objectToJson(ossResMessage));
contractAnnex.setIncomeId(dto.getCollectOrderNo());
contractAnnex.setAnnexName(fileName);
contractAnnex.setAnnexType(annexType);
contractAnnex.setAnnexOssUrl(ossResMessage.getPublicCdnHttp());
contractAnnex.setCreater(CollectServiceConstants.COLLECT_SYSTEM_1);
contractAnnex.setCreatedDate(date);
return contractAnnex;
}
return null;
}
OSS底层代码
OSS上传代码-字符集指定UTF-8 无问题
protected ExecutionContext createDefaultContext(HttpMethod method, String bucketName, String key) {
ExecutionContext context = new ExecutionContext();
context.setCharset(DEFAULT_CHARSET_NAME);
context.setSigner(createSigner(method, bucketName, key, credsProvider.getCredentials()));
context.addResponseHandler(errorResponseHandler);
if (method == HttpMethod.POST) {
context.setRetryStrategy(noRetryStrategy);
}
context.setCredentials(credsProvider.getCredentials());
return context;
}
问题总结
OSS上传没问题只能是下载的时候有问题啦,继续扒底层
源码方法
com.aliyun.oss.OSSClient#generatePresignedUrl(com.aliyun.oss.model.GeneratePresignedUrlRequest)
底层特殊处理啦URL
String queryString = HttpUtil.paramToQueryString(params, DEFAULT_CHARSET_NAME);
OSS官方文档
- 如果Object名称包含星号(*)、正斜线(/)等特殊字符时,可能会出现特殊字符转义的情况。例如,下载
example*.jpg
到本地时,example*.jpg
可能会转义为example_.jpg
。 - 如需确保下载名称中包含中文字符的Object到本地指定路径后,文件名称不出现乱码的现象,您需要将名称中包含的中文字符进行URL编码。例如,将
测试.txt
从OSS下载到本地后,需要保留文件名为测试.txt
,需按照"attachment;filename="+URLEncoder.encode("测试","UTF-8")+".txt;filename*=UTF-8''"+URLEncoder.encode("测试","UTF-8")+".txt"
的格式设置Content-Disposition,即attachment;filename=%E6%B5%8B%E8%AF%95.txt;filename*=UTF-8''%E6%B5%8B%E8%AF%95.txt
解决代码
URLEncoder.encode(fileName, "UTF-8") + ".zip"