我使用Spring Boot创建文件上传服务,并使用Spring Mock Mvc和MockMultipartFile进行测试。我想测试是否在超出最大文件大小时抛出错误。以下测试失败,因为它收到200。
RandomAccessFile f = new RandomAccessFile("t", "rw");
f.setLength(1024 * 1024 * 10);
InputStream is = Channels.newInputStream(f.getChannel());
MockMultipartFile firstFile = new MockMultipartFile("data", "file1.txt", "text/plain", is);
mvc.perform(fileUpload("/files")
.file(firstFile))
.andExpect(status().isInternalServerError());
是否有可能测试上传文件的大小?
答案 0 :(得分:5)
如果length方法返回的文件的当前长度是 小于newLength参数,然后文件将被扩展。在 在这种情况下,文件的扩展部分的内容不是 定义
请改为尝试:
byte[] bytes = new byte[1024 * 1024 * 10];
MockMultipartFile firstFile = new MockMultipartFile("data", "file1.txt", "text/plain", bytes);
答案 1 :(得分:1)
您不能用 Spring 的 MockMultipartFile / MockMvc 测试这个。这样做的原因是错误的根源不在 Spring 本身,而是在底层 Web 服务器(通常是 Tomcat)中,正如您在 MaxUploadSizeExceededException 的堆栈跟踪中所见:
org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size of 500000 bytes exceeded; nested exception is org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1065736) exceeds the configured maximum (500000)
at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:160)
at org.springframework.web.multipart.commons.CommonsMultipartResolver.resolveMultipart(CommonsMultipartResolver.java:139)
[...]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
Caused by: org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1065736) exceeds the configured maximum (500000)
at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:965)
at org.apache.commons.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:310)
at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:334)
at org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:115)
at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:156)
... 20 more
当使用默认的 MockMvc 和 @SpringBootTest
的默认设置时,不会启动真正的 Web 服务器,因此不会发生错误。
然而,您可以通过提供 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
来告诉 Spring 为您的测试启动一个真正的 Web 服务器,这将(意外)在一个随机端口上启动一个 Web 服务器。您可以在测试类中使用 @LocalServerPort
访问该端口。
然后您可以编写一个测试,对您的测试服务器执行真实分段上传,而不是伪造的。 REST Assured 是一个可以执行此操作的库:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyTest {
@LocalServerPort
private int port;
@Test
void testMultipartUpload() throws Exception {
File file = new File("my-file");
RestAssured.baseURI = "http://localhost/api";
RestAssured.port = port;
Response res = given()
.multiPart("data", file, "text/plain")
.when().post("/upload");
...
}
}
当您上传的文件太大时,此测试将显示服务器错误。
答案 2 :(得分:0)
这里有同样的问题。
不知道是否有更好的解决方案,但是我创建了一个注释来验证上传的图像列表,并与其他图像一起进行检查。
public class ImageValidator implements ConstraintValidator<ValidImage, List<MultipartFile>> {
@Override
public boolean isValid(
List<MultipartFile> listMultipartFile, ConstraintValidatorContext context) {
for (var multipartFile : listMultipartFile) {
var msgValidation = imageValidations(multipartFile);
if (!msgValidation.isEmpty()) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(msgValidation).addConstraintViolation();
return false;
}
}
return true;
}
private String imageValidations(MultipartFile multipartFile) {
var contentType = multipartFile.getContentType();
if (!isSupportedContentType(contentType)) {
return String.format(
"Only JPG and PNG images are allowed, %s provided.", multipartFile.getContentType());
} else if (multipartFile.isEmpty()) {
return "It must not be an empty image.";
} else if (multipartFile.getSize() > (1024 * 1024)) {
return "File size should be at most 1MB";
}
return "";
}
private boolean isSupportedContentType(String contentType) {
var supportedContents = List.of("image/jpg", "image/jpeg", "image/png");
return supportedContents.contains(contentType);
}
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {ImageValidator.class})
public @interface ValidImage {
String message() default "Invalid image file";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}