【问题标题】:Spring WebClient corrupts binary dataSpring WebClient 损坏二进制数据
【发布时间】:2022-01-13 16:54:55
【问题描述】:

我正在将包含二进制数据的文件从服务 A 发送到服务 B。当文件数量相对较少(比如说 5 个)时,一切正常。但是,当我尝试发送更多文件(比如说几百个)时,它有时会失败。我试图检查这个二进制数据发生了什么,看起来WebClient 以某种方式破坏了它(最后出现了奇怪的填充)。

我创建了一个可重现的最小示例来说明这个问题。

服务 B 中的端点(使用二进制文件):

@RestController
class FilesController {

    @PostMapping(value = "/files")
    Mono<List<String>> uploadFiles(@RequestBody Flux<Part> parts) {
        return parts
                .filter(FilePart.class::isInstance)
                .map(FilePart.class::cast)
                .flatMap(part -> DataBufferUtils.join(part.content())
                        .map(buffer -> {
                            byte[] data = new byte[buffer.readableByteCount()];
                            buffer.read(data);
                            DataBufferUtils.release(buffer);
                            return Base64.getEncoder().encodeToString(data);
                        })
                )
                .collectList();
    }
}

说明服务 A 如何发送数据的测试:

public class BinaryUploadTest {

    private final CopyOnWriteArrayList<String> sentBytes = new CopyOnWriteArrayList<>();

    @BeforeEach
    void before() {
        sentBytes.clear();
    }

    /**
     * this test passes all the time
     */
    @Test
    void shouldUpload5Files() {
        // given
        MultiValueMap<String, HttpEntity<?>> body = buildResources(5);

        // when
        List<String> receivedBytes = sendPostRequest(body);

        // then
        assertEquals(sentBytes, receivedBytes);
    }

    /**
     * this test fails most of the time
     */
    @Test
    void shouldUpload1000Files() {
        // given
        MultiValueMap<String, HttpEntity<?>> body = buildResources(1000);

        // when
        List<String> receivedBytes = sendPostRequest(body);

        // then
        assertEquals(sentBytes, receivedBytes);
    }

    private List<String> sendPostRequest(MultiValueMap<String, HttpEntity<?>> body) {
        return WebClient.builder().build().post()
                .uri("http://localhost:8080/files")
                .contentType(MediaType.MULTIPART_FORM_DATA)
                .body(BodyInserters.fromMultipartData(body))
                .retrieve()
                .bodyToMono(new ParameterizedTypeReference<List<String>>() {
                })
                .block();
    }

    private MultiValueMap<String, HttpEntity<?>> buildResources(int numberOfResources) {
        MultipartBodyBuilder builder = new MultipartBodyBuilder();
        for (int i = 0; i < numberOfResources; i++) {
            builder.part("item-" + i, buildResource(i));
        }
        return builder.build();
    }

    private ByteArrayResource buildResource(int index) {
        byte[] bytes = randomBytes();
        sentBytes.add(Base64.getEncoder().encodeToString(bytes)); // keeps track of what has been sent
        return new ByteArrayResource(bytes) {
            @Override
            public String getFilename() {
                return "filename-" + index;
            }
        };
    }

    private byte[] randomBytes() {
        byte[] bytes = new byte[ThreadLocalRandom.current().nextInt(16, 32)];
        ThreadLocalRandom.current().nextBytes(bytes);
        return bytes;
    }
}

这种数据损坏的原因可能是什么?

【问题讨论】:

    标签: java spring project-reactor spring-webclient reactor-netty


    【解决方案1】:

    原来是 Spring Framework 中的一个错误(更准确地说是在 MultipartParser 类中)。我创建了一个GitHub issue,它将在下一个版本(5.3.16)中修复。该错误已由this commit 修复。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-26
      • 1970-01-01
      • 1970-01-01
      • 2019-06-19
      • 2013-07-19
      • 1970-01-01
      • 2011-09-04
      • 1970-01-01
      相关资源
      最近更新 更多