在 Spring WebClient 中流上传 'POST'
Stream upload 'POST' in Spring WebClient
我正在使用 WebClient 使用 HTTP 帖子上传(原始字节)数据流:
final byte[] rawData = IOUtils.toByteArray(sourceInputStream);
webClient.post()
.uri(uri)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.bodyValue(rawData)
.exchange()...
我担心可能会使用大量内存,因为有时这些 objects 可能会很大 (~200Mb),所以我想直接从 InputStream 读取并作为流上传。
我试过了:
bodyValue(BodyInserters.fromResource(new InputStreamResource(inputStream)))
但出现异常内容类型 'application/octet-stream' 不支持 bodyType=org.springframework.web.reactive.function.BodyInserters
所以我尝试删除 header 但数据随后被破坏了。
有没有办法在不通过内存 'buffer' rawData[] 的情况下流式传输数据?
谢谢
我最终保留了 rawData[] 缓冲区并在请求中指定了 contentLength:
webClient.post()
.uri(uri)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.contentLength(bytes.length)
.bodyValue(bytes)
.exchange()
.block(Duration.ofSeconds(30));
对于非常大的文件,我进行了分块上传 - 例如 1Mb 的文件块可能如下所示:
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/StartUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678')?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/ContinueUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678',fileOffset=1048576)?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/ContinueUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678',fileOffset=2097152)?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/ContinueUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678',fileOffset=3145728)?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/ContinueUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678',fileOffset=4194304)?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/ContinueUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678',fileOffset=5242880)?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/FinishUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678',fileOffset=6291456)?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
通过处理原始字节来驱动:
final UUID id = UUID.randomUUID();
int offset = 0;
while (offset < bytesTotal) {
final URI uri;
if (offset == 0) {
uri = createStartUri(id, path, filename);
} else if (offset < bytesTotal - CHUNK_SIZE) {
uri = createContinueUri(id, offset, path, filename);
} else {
uri = createFinishUri(id, offset, path, filename);
}
final byte[] bytes = ArrayUtils.subarray(fileDataBytes, offset, offset + CHUNK_SIZE);
webClient.post().uri(uri).contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(bytes.length)
.bodyValue(bytes).exchange().block(Duration.ofSeconds(30));
offset += CHUNK_SIZE;
}
您的第一次尝试几乎是正确的,但是您需要使用 body(...)
而不是 bodyValue(...)
:
body(BodyInserters.fromResource(new InputStreamResource(inputStream)))
这是因为 bodyValue(...)
将您的资源插入器包装在一个值插入器中,然后它将尝试序列化资源插入器本身并因您收到的错误而失败。
我正在使用 WebClient 使用 HTTP 帖子上传(原始字节)数据流:
final byte[] rawData = IOUtils.toByteArray(sourceInputStream);
webClient.post()
.uri(uri)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.bodyValue(rawData)
.exchange()...
我担心可能会使用大量内存,因为有时这些 objects 可能会很大 (~200Mb),所以我想直接从 InputStream 读取并作为流上传。
我试过了:
bodyValue(BodyInserters.fromResource(new InputStreamResource(inputStream)))
但出现异常内容类型 'application/octet-stream' 不支持 bodyType=org.springframework.web.reactive.function.BodyInserters
所以我尝试删除 header 但数据随后被破坏了。
有没有办法在不通过内存 'buffer' rawData[] 的情况下流式传输数据?
谢谢
我最终保留了 rawData[] 缓冲区并在请求中指定了 contentLength:
webClient.post()
.uri(uri)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.contentLength(bytes.length)
.bodyValue(bytes)
.exchange()
.block(Duration.ofSeconds(30));
对于非常大的文件,我进行了分块上传 - 例如 1Mb 的文件块可能如下所示:
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/StartUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678')?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/ContinueUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678',fileOffset=1048576)?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/ContinueUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678',fileOffset=2097152)?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/ContinueUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678',fileOffset=3145728)?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/ContinueUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678',fileOffset=4194304)?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/ContinueUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678',fileOffset=5242880)?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
POST: https://sharepoint.acme.com:443/sharepoint-site/_api/web/GetFileByServerRelativeUrl(@f)/FinishUpload(uploadId=guid'680f93eb-8c6a-443d-87ad-1e1b324ea678',fileOffset=6291456)?@f='/sharepoint-site/folderA/subFolderB/etc/example.file'
通过处理原始字节来驱动:
final UUID id = UUID.randomUUID();
int offset = 0;
while (offset < bytesTotal) {
final URI uri;
if (offset == 0) {
uri = createStartUri(id, path, filename);
} else if (offset < bytesTotal - CHUNK_SIZE) {
uri = createContinueUri(id, offset, path, filename);
} else {
uri = createFinishUri(id, offset, path, filename);
}
final byte[] bytes = ArrayUtils.subarray(fileDataBytes, offset, offset + CHUNK_SIZE);
webClient.post().uri(uri).contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(bytes.length)
.bodyValue(bytes).exchange().block(Duration.ofSeconds(30));
offset += CHUNK_SIZE;
}
您的第一次尝试几乎是正确的,但是您需要使用 body(...)
而不是 bodyValue(...)
:
body(BodyInserters.fromResource(new InputStreamResource(inputStream)))
这是因为 bodyValue(...)
将您的资源插入器包装在一个值插入器中,然后它将尝试序列化资源插入器本身并因您收到的错误而失败。