You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Postman多部分文件数据编码问题及Java请求适配咨询

问题根源与解决方案

首先得明确:你看到Postman里的PDF内容并不是什么编码后的字符串,而是原始二进制字节的可视化展示——Postman发送附件时,是直接把文件的原始二进制字节放到multipart请求的对应part里,根本没有做字符编码转换。你手动用字符串读取二进制文件的做法,从一开始就破坏了文件的原始字节结构,这才是附件损坏的核心原因。

为什么你的代码会出问题

不管你用UTF-8、ISO_8859_1还是其他编码,FileUtils.readFileToString都是把二进制字节序列尝试转换成字符序列,再把字符序列转成字节序列写入请求。但二进制文件(比如PDF、图片)的字节流并不是合法的字符编码序列,转换过程中必然会丢失或篡改原始字节,导致文件损坏。

正确的做法:用Spring的Multipart工具类构造请求

Spring的RestTemplate本身就支持构造multipart请求,完全不需要手动拼接字符串。直接用MultipartBodyBuilder来构建各个form part,包括文本字段和二进制附件,Spring会自动处理multipart的边界、Content-Type等细节,也会正确传输文件的原始字节。

修改后的代码示例:

class SendMailAdaptorImpl implements SendMailAdaptor {
    private static final String PART_DELIMITER = "Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL";
    private final String channelId = "...";
    private final String endpoint = "...";
    private final RestTemplate restTemplate = new RestTemplate();

    @Override
    public APIMailResponse sendMail(String from, String to, String templateId, List<String> templateParameters, File attachment) {
        // 使用MultipartBodyBuilder构建请求体
        MultipartBodyBuilder builder = MultipartBodyBuilder.create();
        // 添加文本字段
        builder.part("to", to);
        builder.part("from", from);
        builder.part("template_id", templateId);
        builder.part("channel_id", this.channelId);
        builder.part("template_parameter", constructTemplateParameter(templateParameters));
        
        // 添加附件(如果存在)
        if (attachment != null) {
            // 用FileSystemResource包装文件,Spring会自动处理二进制传输
            FileSystemResource resource = new FileSystemResource(attachment);
            builder.part("attach_file_content", resource)
                    .header("Content-Disposition", "form-data; name=\"attach_file_content\"; filename=\"" + attachment.getName() + "\"")
                    .header("Content-Type", guessAttachmentMimeType(attachment));
            builder.part("attach_file_name", attachment.getName());
        }

        // 设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA.withParameter("boundary", PART_DELIMITER));
        
        // 构造请求实体
        HttpEntity<MultiValueMap<String, HttpEntity<?>>> request = new HttpEntity<>(builder.build(), headers);
        
        URI uri = UriComponentsBuilder.fromHttpUrl(endpoint)
                .build().encode(StandardCharsets.UTF_8).toUri();
        
        return restTemplate.postForObject(uri, request, APIMailResponse.class);
    }

    private String guessAttachmentMimeType(File attachment) {
        try {
            return Files.probeContentType(attachment.toPath());
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    private String constructTemplateParameter(List<String> templateParameters) {
        return templateParameters.stream().reduce("Name", (a, b) -> a + "|" + b);
    }
}

关键说明

  • 二进制文件必须以字节流的形式传输,绝对不能转换成字符串处理——字符串是用来处理文本数据的,不是二进制数据。
  • 不存在“硬编码某个编码适配各类文件”的可能,因为二进制文件本身没有字符编码的概念,只有文本文件才需要考虑编码。
  • 用Spring提供的multipart工具类,不仅能避免手动拼接的错误,还能自动处理边界冲突、编码等细节,比手动构造字符串可靠得多。

内容的提问来源于stack exchange,提问作者asinkxcoswt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:28:36