我有一个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-stream
的PUT
或POST
。您可以获取裸请求输入流,并将其传递给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));
}
}
也许您还可以使用MultipartFile
的getBytes
方法,并创建一个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
);
我更喜欢第一种解决方案,但请尝试确定哪种方案能为您提供最佳性能。