用于文件上传的Spring引导API的Tomcat性能
我有一个SpringBootAPI,其中一个端点允许用户上传视频。现在,我的控制器基本上将该文件作为一个多部分文件,然后将其存储在tomcat可以访问的临时文件夹中。一旦我将它存储在磁盘上,我就会将视频推送到S3存储桶中 现在对我来说,这似乎不是最理想的,比如如果我想让100或1000个用户同时上传,那么首先将文件写入磁盘似乎真的没有什么效果 作为一个小背景,我将它存储在磁盘上,目的是如果有问题推到S3,我可以重试 下面的代码可能会显示我比上面做得更好的地方:用于文件上传的Spring引导API的Tomcat性能,spring,spring-boot,performance,tomcat,amazon-s3,Spring,Spring Boot,Performance,Tomcat,Amazon S3,我有一个SpringBootAPI,其中一个端点允许用户上传视频。现在,我的控制器基本上将该文件作为一个多部分文件,然后将其存储在tomcat可以访问的临时文件夹中。一旦我将它存储在磁盘上,我就会将视频推送到S3存储桶中 现在对我来说,这似乎不是最理想的,比如如果我想让100或1000个用户同时上传,那么首先将文件写入磁盘似乎真的没有什么效果 作为一个小背景,我将它存储在磁盘上,目的是如果有问题推到S3,我可以重试 下面的代码可能会显示我比上面做得更好的地方: public Video addV
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没有其他方法。multipart的问题是,为了正确地将零件与需求分开,有时需要跳过或重复。在没有内存爆炸的内存中,这是不可能的。因此,Commons FileUpload会在达到某个阈值后将其缓存到磁盘上。 多部分请求是最糟糕的方式。我强烈建议对内容类型
application/octet-stream
使用PUT
或POST
。您可以获取裸请求输入流,并将其传递给HttpClient,以流式传输到后端服务器。我在5年前就这样做了,它可以工作千兆字节。我已经在ApacheHttpClient邮件列表中发布了解决方案
在特定条件下,这有一种可能会起作用:
- 所有零件都符合您想要阅读的正确物理顺序
- 您对后端的写入速度足以支持前端的读取
使用根部分,然后转到下一个物理部分,惰性地处理请求主体。JAX-WSRI(Metro)可以很好地处理XOP/MTOM的多部分请求。从中学习,因为您无法使它变得更好。也许您可以尝试将输入流从
多部分文件
定向到S3
考虑以下uploadFileToS3Bucket
方法:
public PutObjectResult uploadFileToS3Bucket(InputStream输入,长码,字符串标题,字符串描述){
//指示信息的长度,以避免AWS SDK计算信息
//见: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=新的ObjectMetadata();
objectMetadata.setContentLength(size);//依赖于Spring实现。也许您也可以使用input.available()
//根据需要计算对象名称
字符串键=“…”;
PutObjectRequest PutObjectRequest=新建PutObjectRequest(
this.awsS3Bucket、key、input、objectMetadata
);
//代码的其余部分
如果(启用PublicReadAccess){
putObjectRequest.withCannedAcl(CanneDaccessController.PublicRead);
}
//将文件作为具有ContentType和标题的新对象上载
返回指定的.amazonS3.putObject(putObjectRequest);
}
当然,您需要为服务提供从与multipartfile
对象关联的客户端请求中获得的输入流:
公共视频添加视频(
@RequestParam(“标题”)字符串标题,
@RequestParam(“描述”)字符串描述,
@RequestParam(value=“file”,required=true)多部分文件(file){
尝试(InputStream输入=file.getInputStream()){
this.amazonS3ClientService.uploadFileToS3Bucket(输入,file.getSize(),title,description));
}
}
您可能还可以使用MultipartFile
的getBytes
方法,创建一个ByteArrayInputStream
来执行该操作
在添加视频中
:
byte[]bytes=file.getBytes();
在上传文件到3bucket
:
ObjectMetadata ObjectMetadata=new ObjectMetadata();
objectMetadata.setContentLength(bytes.length);
PutObjectRequest PutObjectRequest=新建PutObjectRequest(
this.awsS3Bucket,key,new ByteArrayInputStream(字节),objectMetadata
);
我更喜欢第一种解决方案,但请尝试确定哪个选项为您提供了最佳性能。您能找到邮件列表的链接吗?这听起来很有趣。@nanomader它需要一个自定义/包装的输入流。如果您想这样做(例如,如果您需要同时上载文档和其他表单数据),您仍然可以执行多部分操作,但您必须阻止Tomcat为您处理解析。相反,您必须自己解析请求,直接读取请求的
InputStream
,并根据需要进行流式处理。问题是,您无法保证元数据(例如文件名)在有效负载(文件字节)之前到达,因此您可能必须编写大量“异常”处理代码(在异常工作流的意义上,而不是抛出的异常)而我的评论只是对如何做到这一点的一般性解释。但正如他所建议的:如果你不需要的话,为什么要重新发明轮子呢。从JAX-WSRI学习并使用MIME解析器依赖项,与SOAP没有任何关系。