用于文件上载的带有Spring Boot API的Tomcat性能



我有一个Spring引导API,其中一个端点允许用户上传视频。现在,我的控制器基本上将该文件作为MultiPart文件,然后将其存储在tomcat可以访问的临时文件夹中。一旦我把它存储在磁盘上,我就会把视频推到S3存储桶中。

无论如何,现在对我来说,这似乎不是最佳的,就像如果我想让100或1000个用户同时上传,那么首先将文件写入磁盘似乎真的很不划算。

作为一个小背景,我将其存储在磁盘上,目的是如果在推送S3时出现问题,我可以重试

下面的代码可能会显示我比上面做得更好的地方:

public Video addVideo(@RequestParam("title") String title,
@RequestParam("Description") String Description,
@RequestParam(value = "file", required = true) MultipartFile file) {         
this.amazonS3ClientService.uploadFileToS3Bucket(file, title, description));
}

存储视频文件的方法:

String fileNameWithExtenstion = awsS3FileName + "." + FilenameUtils.getExtension(multipartFile.getOriginalFilename());
//creating the file in the server (temporarily)
File file = new File(tomcatTempDir + fileNameWithExtenstion);FileOutputStream fos = new FileOutputStream(file);
fos.write(multipartFile.getBytes());
fos.close();PutObjectRequest putObjectRequest = new PutObjectRequest(this.awsS3Bucket, awsS3BucketFolder + UnigueId + "/" + fileNameWithExtenstion, file);
if (enablePublicReadAccess) {
putObjectRequest.withCannedAcl(CannedAccessControlList.PublicRead);
}
// Upload a file as a new object with ContentType and title 
specified.amazonS3.putObject(putObjectRequest);
//removing the file created in the server
file.delete();

因此,我的问题是……Tomcat中是否有更好的方法:

A( 通过控制器接收文件B(推送至S3

没有其他方法可以使用multipart。多部分的问题是,为了正确地从需求中分割部分,有时会跳过或重复。在没有内存爆炸的情况下,这在内存中是不可能的。因此,Commons FileUpload会在达到某个阈值后将它们缓存在磁盘上。多部分请求是最糟糕的方式。强烈建议使用内容类型为application/octet-streamPUTPOST。您可以获取裸请求输入流,并将其传递给HttpClient以流式传输到后端服务器。我在5年前就已经做过了,它可以工作千兆字节。我已经在ApacheHttpClient邮件列表中发布了该解决方案。

有一种可能是在特定条件下如何工作的:

  • 所有部件都按您想要读取的正确物理顺序排列
  • 您对后端的写入速度足够快,可以维持前端的读取

消耗根部分,然后转到下一个物理部分,懒洋洋地处理请求体。JAX-WSRI(Metro(对XOP/MTOM的多部分请求有很好的处理。从中吸取教训,因为你无法让它变得更好。

也许您可以尝试将输入流从MultipartFile直接流式传输到S3。

考虑以下uploadFileToS3Bucket方法:

public PutObjectResult uploadFileToS3Bucket(InputStream input, long size, String title, String description) {
// Indicate the length of the information to avoid the need to compute it by the AWS SDK
// See: https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/model/PutObjectRequest.html#PutObjectRequest-java.lang.String-java.lang.String-java.io.InputStream-com.amazonaws.services.s3.model.ObjectMetadata-
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(size); // rely on Spring implementation. Maybe you probably also can use input.available()
// compute the object name as appropriate
String key = "...";
PutObjectRequest putObjectRequest = new PutObjectRequest(
this.awsS3Bucket, key, input, objectMetadata
);
// The rest of your code 
if (enablePublicReadAccess) {
putObjectRequest.withCannedAcl(CannedAccessControlList.PublicRead);
}
// Upload a file as a new object with ContentType and title 
return specified.amazonS3.putObject(putObjectRequest);
}

当然,您需要向服务提供从与MutipartFile对象关联的客户端请求中获得的输入流:

public Video addVideo(
@RequestParam("title") String title,
@RequestParam("Description") String Description,
@RequestParam(value = "file", required = true) MultipartFile file) { 
try (InputStream input = file.getInputStream()) {        
this.amazonS3ClientService.uploadFileToS3Bucket(input, file.getSize(), title, description));
}
}

也许您还可以使用MultipartFilegetBytes方法,并创建一个ByteArrayInputStream来执行操作。

addVideo:中

byte[] bytes = file.getBytes();

uploadFileToS3Bucket:中

ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(bytes.length);
PutObjectRequest putObjectRequest = new PutObjectRequest(
this.awsS3Bucket, key, new ByteArrayInputStream(bytes), objectMetadata
);

我更喜欢第一种解决方案,但请尝试确定哪种方案能为您提供最佳性能。

最新更新