大文件上传后端多个pod
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大文件上传后端多个pod相关的知识,希望对你有一定的参考价值。
后台使用springboot实现,主要使用一个数组来判断文件是否完成上传,当每次上传一个信的文件时根据uid在缓存中新建或者将本次的文件块信息记录至缓存中@Getter
@Setter
@Builder
public static class FileInfo
// 路径
private String filePath;
// 文件名
private String fileName;
// uid
private String uid;
private Integer index;
private Integer chunksLength;
private Boolean uploaded;
// key为文件的uid,value为Boolean[] 表示每个文件块是否上传完成
private static Map<String, FileInfo[]> fileUploadedCache = new HashMap<>();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
当根据数组判断所有文件块都上传完成时,清除缓存中uid对应的数组,并合并文件,清除子文件块。
demo
在这里插入图片描述
上传文件块大小限制问题
在测试的时候发现当文件块的大小大于10M时,报org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException异常
the request was rejected because its size (10486377) exceeds the configured maximum (10485760)
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.init(FileItemIteratorImpl.java:150) ~[tomcat-embed-core-9.0.34.jar:9.0.34]
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.getMultiPartStream(FileItemIteratorImpl.java:194) ~[tomcat-embed-core-9.0.34.jar:9.0.34]
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.findNextItem(FileItemIteratorImpl.java:213) ~[tomcat-embed-core-9.0.34.jar:9.0.34]
1
2
3
4
1
2
3
4
找了下springboot的文档中关于自动配置的类,找到了MultipartAutoConfiguration,文档地址
https://docs.spring.io/spring-boot/docs/2.2.7.RELEASE/reference/html/appendix-auto-configuration-classes.html#auto-configuration-classes
MultipartAutoConfiguration类的具体内容如下:
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.web.servlet;
import javax.servlet.MultipartConfigElement;
import javax.servlet.Servlet;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.DispatcherServlet;
/**
* @link EnableAutoConfiguration Auto-configuration for multi-part uploads. Adds a
* @link StandardServletMultipartResolver if none is present, and adds a
* @link javax.servlet.MultipartConfigElement multipartConfigElement if none is
* otherwise defined. The @link ServletWebServerApplicationContext will associate the
* @link MultipartConfigElement bean to any @link Servlet beans.
* <p>
* The @link javax.servlet.MultipartConfigElement is a Servlet API that's used to
* configure how the server handles file uploads.
*
* @author Greg Turnquist
* @author Josh Long
* @author Toshiaki Maki
* @since 2.0.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass( Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class )
@ConditionalOnProperty(prefix = "spring.servlet.multipart", name = "enabled", matchIfMissing = true)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration
private final MultipartProperties multipartProperties;
public MultipartAutoConfiguration(MultipartProperties multipartProperties)
this.multipartProperties = multipartProperties;
@Bean
@ConditionalOnMissingBean( MultipartConfigElement.class, CommonsMultipartResolver.class )
public MultipartConfigElement multipartConfigElement()
return this.multipartProperties.createMultipartConfig();
@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
@ConditionalOnMissingBean(MultipartResolver.class)
public StandardServletMultipartResolver multipartResolver()
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
return multipartResolver;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
重点关注下面这个配置方法
@Bean
@ConditionalOnMissingBean( MultipartConfigElement.class, CommonsMultipartResolver.class )
public MultipartConfigElement multipartConfigElement()
return this.multipartProperties.createMultipartConfig();
1
2
3
4
5
1
2
3
4
5
这里的this.multipartProperties.createMultipartConfig()创建了springboot默认的文件上传的配置参数,跳转到createMultipartConfig方法里可以看到
public MultipartConfigElement createMultipartConfig()
MultipartConfigFactory factory = new MultipartConfigFactory();
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(this.fileSizeThreshold).to(factory::setFileSizeThreshold);
map.from(this.location).whenHasText().to(factory::setLocation);
map.from(this.maxRequestSize).to(factory::setMaxRequestSize);
map.from(this.maxFileSize).to(factory::setMaxFileSize);
return factory.createMultipartConfig();
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
看这里的map.from(this.maxFileSize).to(factory::setMaxFileSize);继续查看this.maxFileSize看到private DataSize maxFileSize = DataSize.ofMegabytes(1L)查看ofMegabytes方法,可以看到
public static DataSize ofMegabytes(long megabytes)
return new DataSize(Math.multiplyExact(megabytes, 1048576L));
1
2
3
1
2
3
到这里,终于看到默认配置的上传文件大小限制1048576了,那么怎么解除这个限制呢?有两种办法:
在前端分割文件的时候,设置文件块的最大大小要小于1048576
在后台代码新建一个配置类,配置类里配置MultipartConfigElement类,代码如下:
@Bean
public MultipartConfigElement multipartConfigElement()
// 不限制上传文件的大小
return new MultipartConfigElement("");
1
2
3
4
5
1
2
3
4
5
这里MultipartConfigElement("")构造方法内容如下:
public MultipartConfigElement(String location)
if (location != null)
this.location = location;
else
this.location = "";
this.maxFileSize = -1L;
this.maxRequestSize = -1L;
this.fileSizeThreshold = 0;
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
这里设置maxFileSize和maxRequestSize为-1表示不限制上传文件大小,这里的-1表示不限制上传文件大小的原因见org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.init方法, 参考技术A 大文件上传后端多个pod?答:k8s访问cephfs的方式之一便是通过ceph-csi访问。ceph-csi提供了动态和静态的方式。
静态方式 由存储管理员显式地创建pv,开发者通过pvc获得符合容量要求和访问模式的pv,并和pod关联,达到使用存储的要求。但这样存在一些弊端,比如需要使用不同的pv时,管理员要分别手动构建pv。
动态方式 存储管理员不需要显式地创建pv,而是创建storageClass。由开发者声明pvc,由storageClass生成合适的pv。但是pv关联的路径使用uuid规则创建,如需将后台存储的目录和pod对应并不是很直观。
搭建了一个minikube的k8s系统环境(版本V1.21.0)和ceph存储(版本16.2.4),完成ceph-csi的配置。
不需要配置storageClass。只需要配置一个pv、一个pvc和两个pod。
在cephfs后端创建一个可使用的子卷,实质上是在cephfs内的/volumes(该目录是ceph-csi默认使用的目录)下面创建一系列子目录用来共享。
首先创建一个子卷的组(相当于创建一个上层目录,后面的子卷都是该目录下的子目录)。命令如下:
该命令的源格式为:
这条命令简单的效果是在cephfs后台会创建一个 /volumes/data 的路径。以后所有属于该组的子卷都在该目录下。
然后创建一个子卷名称为data,该卷的大小为1GB大小,size的单位是bytes。命令如下:
该命令的源格式为:
文件系统后台实际创建 /volumes/data/logs/dda798fb-2160-4aca-b810-3bbf7bbdd394 ,即在子卷下面还有一个子目录,目录使用uuid命名。该uuid命名的目录配置了和size一致的配额。即pv的大小。
该pv文件名称为 cephfs-static-pv1.yaml 。pv里的内容:
1、 storageClassName: standard ,如果不加该句,pvc在apply之后会一直处于pending状态,通过describe pvc 可以看到 “Cannot bind to requested volume storageClassName: does not match” 的消息。官方文档少了这条说明。
2、 persistentVolumeReclaimPolicy: Retain ,当 参考技术B 是的。因为搭建了一个minikube的k8s系统环境(版本V1.21.0)和ceph存储(版本16.2.4),完成ceph-csi的配置。
不需要配置storageClass。只需要配置一个pv、一个pvc和两个pod。 参考技术C 解决方法:
问题原因: IIS 默认限制了交互最大内容长度(字节)。默认值为:30 000 000。
所以解决问题的关键就是突破这种限制了。
其中一种修改步骤如下图:(改成最大 3G)
以上是关于大文件上传后端多个pod的主要内容,如果未能解决你的问题,请参考以下文章