OSS下载中文名编码错误

145 阅读2分钟

前言

交易单关联合同订单,合同附件打包压缩,在运营端反显附件名称,运营端人员下载查看附件 感觉很简单,附件上传下载,入库

事故经过

附件在运营端显示乱码

企业微信截图_17255298639965.png

排查过程

一开始以为前段代码有问题,可能对应的## Content-Type类型不对,后来发现没问题,那就只能是后端代码问题啦,就开始撸底层代码

# 如何设置Content-Type(MIME)?

阿里云文档地址

help.aliyun.com/zh/oss/user…

文件扩展名Content-Type(Mime-Type)文件扩展名Content-Type(Mime-Type)
.tarapplication/x-tar.zipapplication/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"

阿里云相关文档地址

help.aliyun.com/zh/oss/user…