You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2020/08/05 00:52:39 UTC

[servicecomb-java-chassis] 01/02: [SCB-2058] fix SpringMVC provider not support non-file-type RequestPart problem

This is an automated email from the ASF dual-hosted git repository.

liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git

commit 09746aac548b73a8b472183625a591c586b3c52e
Author: yhs0092 <yh...@163.com>
AuthorDate: Mon Aug 3 19:13:18 2020 +0800

    [SCB-2058] fix SpringMVC provider not support non-file-type RequestPart problem
    
    - add MultipartFilePropertyCreator to enable ModelConverters resolving MultipartFile param
    - make RequestPartAnnotationProcessor recognize non-file-type RequestPart param
---
 .../apache/servicecomb/it/testcase/TestUpload.java |  82 ++++++++++
 .../servicecomb/it/schema/UploadJaxrsSchema.java   |  30 ++++
 .../it/schema/UploadSpringmvcSchema.java           |  15 ++
 .../annotation/RequestPartAnnotationProcessor.java |  34 ++++-
 .../creator/MultipartFilePropertyCreator.java      |  38 +++++
 ...swagger.extend.property.creator.PropertyCreator |  18 +++
 .../RequestPartAnnotationProcessorTest.java        | 169 +++++++++++++++++++++
 .../creator/MultipartFilePropertyCreatorTest.java  |  42 +++++
 8 files changed, 423 insertions(+), 5 deletions(-)

diff --git a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestUpload.java b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestUpload.java
index 8aede2f..57a7a88 100644
--- a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestUpload.java
+++ b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestUpload.java
@@ -20,19 +20,25 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.servicecomb.it.Consumers;
+import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.springframework.core.ParameterizedTypeReference;
 import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
 import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 
 public class TestUpload {
 
@@ -47,6 +53,10 @@ public class TestUpload {
   private static final String message = "cseMessage";
 
   interface UploadIntf {
+    Map<String, String> uploadMultiformMix(Resource file,
+        List<Resource> fileList,
+        String str,
+        List<String> strList);
   }
 
   private static Consumers<UploadIntf> consumersSpringmvc = new Consumers<>("uploadSpringmvcSchema",
@@ -316,6 +326,78 @@ public class TestUpload {
     Assert.assertTrue(containsAll(result, "hello1", "cse4", "cse3", "中文 2", message));
   }
 
+  @Test
+  public void testUploadMultiformMix_RestTemplate_SpringMVC() {
+    Map<String, Object> map = new HashMap<>();
+    List<Resource> fileList = new ArrayList<>();
+    fileList.add(fileSystemResource2);
+    map.put("file", fileSystemResource1);
+    map.put("fileList", fileList);
+    map.put("str", message);
+    map.put("strList", Collections.singletonList("2.中文测试"));
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+    ResponseEntity<Map<String, String>> response =
+        consumersSpringmvc.getSCBRestTemplate().exchange("/uploadMultiformMix", HttpMethod.POST,
+            new HttpEntity<>(map, headers), new ParameterizedTypeReference<Map<String, String>>() {
+            });
+    Map<String, String> responseBody = response.getBody();
+    Assert.assertThat(responseBody, Matchers.notNullValue());
+    Assert.assertThat(responseBody.get("file"), Matchers.is("hello1"));
+    Assert.assertThat(responseBody.get("fileList"), Matchers.is("中文 2"));
+    Assert.assertThat(responseBody.get("str"), Matchers.is("cseMessage"));
+    Assert.assertThat(responseBody.get("strList"), Matchers.is("[2.中文测试]"));
+  }
+
+  @Test
+  public void testUploadMultiformMix_Rpc_SpringMVC() {
+    List<Resource> fileList = new ArrayList<>();
+    fileList.add(fileSystemResource2);
+    Map<String, String> responseBody =
+        consumersSpringmvc.getIntf().uploadMultiformMix(
+            fileSystemResource1, fileList, message, Collections.singletonList("2.中文测试"));
+    Assert.assertThat(responseBody.get("file"), Matchers.is("hello1"));
+    Assert.assertThat(responseBody.get("fileList"), Matchers.is("中文 2"));
+    Assert.assertThat(responseBody.get("str"), Matchers.is("cseMessage"));
+    Assert.assertThat(responseBody.get("strList"), Matchers.is("[2.中文测试]"));
+  }
+
+  @Test
+  public void testUploadMultiformMix_RestTemplate_JAXRS() {
+    Map<String, Object> map = new HashMap<>();
+    List<FileSystemResource> fileList = new ArrayList<>();
+    fileList.add(fileSystemResource2);
+    map.put("file", fileSystemResource1);
+    map.put("fileList", fileList);
+    map.put("str", message);
+    map.put("strList", Collections.singletonList("2.中文测试"));
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+    ResponseEntity<Map<String, String>> response =
+        consumersJaxrs.getSCBRestTemplate().exchange("/uploadMultiformMix", HttpMethod.POST,
+            new HttpEntity<>(map, headers), new ParameterizedTypeReference<Map<String, String>>() {
+            });
+    Map<String, String> responseBody = response.getBody();
+    Assert.assertThat(responseBody, Matchers.notNullValue());
+    Assert.assertThat(responseBody.get("file"), Matchers.is("hello1"));
+    Assert.assertThat(responseBody.get("fileList"), Matchers.is("中文 2"));
+    Assert.assertThat(responseBody.get("str"), Matchers.is("cseMessage"));
+    Assert.assertThat(responseBody.get("strList"), Matchers.is("[2.中文测试]"));
+  }
+
+  @Test
+  public void testUploadMultiformMix_Rpc_JAXRS() {
+    List<Resource> fileList = new ArrayList<>();
+    fileList.add(fileSystemResource2);
+    Map<String, String> responseBody =
+        consumersJaxrs.getIntf().uploadMultiformMix(
+            fileSystemResource1, fileList, message, Collections.singletonList("2.中文测试"));
+    Assert.assertThat(responseBody.get("file"), Matchers.is("hello1"));
+    Assert.assertThat(responseBody.get("fileList"), Matchers.is("中文 2"));
+    Assert.assertThat(responseBody.get("str"), Matchers.is("cseMessage"));
+    Assert.assertThat(responseBody.get("strList"), Matchers.is("[2.中文测试]"));
+  }
+
   private static boolean containsAll(String str, String... strings) {
     for (String string : strings) {
       if (!str.contains(string)) {
diff --git a/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/UploadJaxrsSchema.java b/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/UploadJaxrsSchema.java
index 0f343e2..ad41f38 100644
--- a/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/UploadJaxrsSchema.java
+++ b/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/UploadJaxrsSchema.java
@@ -18,8 +18,11 @@ package org.apache.servicecomb.it.schema;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import javax.servlet.http.Part;
 import javax.ws.rs.FormParam;
@@ -30,6 +33,7 @@ import javax.ws.rs.core.MediaType;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.springframework.web.multipart.MultipartFile;
 
 @RestSchema(schemaId = "uploadJaxrsSchema")
 @Path("/v1/uploadJaxrsSchema")
@@ -138,4 +142,30 @@ public class UploadJaxrsSchema {
     }
     return "";
   }
+
+  @Path("/uploadMultiformMix")
+  @POST
+  public Map<String, String> uploadMultiformMix(@FormParam("file") MultipartFile file,
+      @FormParam("fileList") List<MultipartFile> fileList,
+      @FormParam("str") String str,
+      @FormParam("strList") List<String> strList) throws IOException {
+    HashMap<String, String> map = new HashMap<>();
+    map.put("file", new String(file.getBytes(), StandardCharsets.UTF_8.name()));
+    map.put("fileList", _fileUpload(fileList));
+    map.put("str", str);
+    map.put("strList", strList.toString());
+    return map;
+  }
+
+  private static String _fileUpload(List<MultipartFile> fileList) {
+    StringBuilder stringBuilder = new StringBuilder();
+    try {
+      for (MultipartFile multipartFile : fileList) {
+        stringBuilder.append(IOUtils.toString(multipartFile.getBytes(), StandardCharsets.UTF_8.name()));
+      }
+    } catch (IOException e) {
+      throw new IllegalArgumentException(e);
+    }
+    return stringBuilder.toString();
+  }
 }
diff --git a/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/UploadSpringmvcSchema.java b/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/UploadSpringmvcSchema.java
index 90d24f4..ff29bfc 100644
--- a/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/UploadSpringmvcSchema.java
+++ b/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/UploadSpringmvcSchema.java
@@ -20,7 +20,9 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.servicecomb.provider.rest.common.RestSchema;
@@ -96,6 +98,19 @@ public class UploadSpringmvcSchema {
     return _fileUpload(file1) + name;
   }
 
+  @RequestMapping(path = "/uploadMultiformMix", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+  public Map<String, String> uploadMultiformMix(@RequestPart(name = "file") MultipartFile file,
+      @RequestPart(name = "fileList") List<MultipartFile> fileList,
+      @RequestPart("str") String str,
+      @RequestPart("strList") List<String> strList) throws IOException {
+    HashMap<String, String> map = new HashMap<>();
+    map.put("file", new String(file.getBytes(), StandardCharsets.UTF_8.name()));
+    map.put("fileList", _fileUpload(fileList));
+    map.put("str", str);
+    map.put("strList", strList.toString());
+    return map;
+  }
+
   private static String _fileUpload(List<MultipartFile> fileList) {
     StringBuilder stringBuilder = new StringBuilder();
     try {
diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessor.java
index 4b381ec..3041527 100644
--- a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessor.java
+++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessor.java
@@ -23,13 +23,14 @@ import org.apache.servicecomb.swagger.generator.ParameterProcessor;
 import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType;
 import org.springframework.web.bind.annotation.RequestPart;
 
+import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.type.TypeFactory;
 
+import io.swagger.converter.ModelConverters;
 import io.swagger.models.Operation;
 import io.swagger.models.Swagger;
 import io.swagger.models.parameters.FormParameter;
 import io.swagger.models.properties.ArrayProperty;
-import io.swagger.models.properties.FileProperty;
 import io.swagger.models.properties.Property;
 
 public class RequestPartAnnotationProcessor implements
@@ -56,11 +57,34 @@ public class RequestPartAnnotationProcessor implements
   @Override
   public void fillParameter(Swagger swagger, Operation operation, FormParameter formParameter, Type type,
       RequestPart requestPart) {
-    Property property = new FileProperty();
-    if (TypeFactory.defaultInstance().constructType(type).isContainerType()) {
-      property = new ArrayProperty(new FileProperty());
-    }
+    Property property = resolveParamProperty(type);
+
     formParameter.setProperty(property);
     formParameter.setRequired(requestPart.required());
   }
+
+  private Property resolveParamProperty(Type type) {
+    JavaType javaType = TypeFactory.defaultInstance().constructType(type);
+    if (javaType.isContainerType()) {
+      return resolvePropertyAsContainerType(javaType);
+    }
+    return ModelConverters.getInstance().readAsProperty(type);
+  }
+
+  private Property resolvePropertyAsContainerType(JavaType javaType) {
+    // At present, only array and collection of Part params are supported,
+    // but Map type is also a kind of container type.
+    // Although Map is not supported now, we still consider to take the type of value to generate a property.
+    // Therefore, here we use lastContainedTypeIndex to get the contained type.
+    int lastContainedTypeIndex = javaType.containedTypeCount() - 1;
+    JavaType containedItemType;
+    if (lastContainedTypeIndex < 0) {
+      // javaType may be an array
+      containedItemType = javaType.getContentType();
+    } else {
+      containedItemType = javaType.containedType(lastContainedTypeIndex);
+    }
+    Property containedItemProperty = ModelConverters.getInstance().readAsProperty(containedItemType);
+    return new ArrayProperty(containedItemProperty);
+  }
 }
diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreator.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreator.java
new file mode 100644
index 0000000..8e9f217
--- /dev/null
+++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreator.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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.apache.servicecomb.swagger.generator.springmvc.property.creator;
+
+import org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator;
+import org.springframework.web.multipart.MultipartFile;
+
+import io.swagger.models.properties.FileProperty;
+import io.swagger.models.properties.Property;
+
+public class MultipartFilePropertyCreator implements PropertyCreator {
+  private final Class<?>[] classes = {MultipartFile.class};
+
+  @Override
+  public Property createProperty() {
+    return new FileProperty();
+  }
+
+  @Override
+  public Class<?>[] classes() {
+    return classes;
+  }
+}
diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator
new file mode 100644
index 0000000..97564b8
--- /dev/null
+++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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
+#
+#     http://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.
+#
+
+org.apache.servicecomb.swagger.generator.springmvc.property.creator.MultipartFilePropertyCreator
\ No newline at end of file
diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessorTest.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessorTest.java
new file mode 100644
index 0000000..95a1b40
--- /dev/null
+++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessorTest.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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.apache.servicecomb.swagger.generator.springmvc.processor.annotation;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.List;
+
+import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.multipart.MultipartFile;
+
+import io.swagger.models.parameters.FormParameter;
+import io.swagger.models.properties.FileProperty;
+import io.swagger.models.properties.StringProperty;
+
+public class RequestPartAnnotationProcessorTest {
+  private static Method producerMethod;
+
+  private static RequestPartAnnotationProcessor requestPartAnnotationProcessor = new RequestPartAnnotationProcessor();
+
+  @BeforeClass
+  public static void beforeClass() {
+    for (Method method : DemoRest.class.getDeclaredMethods()) {
+      if (method.getName().equals("fun")) {
+        producerMethod = method;
+        break;
+      }
+    }
+  }
+
+  @Test
+  public void getProcessType() {
+    Assert.assertEquals(requestPartAnnotationProcessor.getProcessType(),
+        RequestPart.class);
+  }
+
+  @Test
+  public void getParameterName_fromValue() {
+    Parameter[] parameters = producerMethod.getParameters();
+
+    Parameter stringParam = parameters[0];
+    RequestPart stringParamAnnotation = stringParam.getAnnotation(RequestPart.class);
+    Assert.assertThat(requestPartAnnotationProcessor.getParameterName(stringParamAnnotation),
+        Matchers.is("stringParam"));
+  }
+
+  @Test
+  public void getParameterName_fromName() {
+    Parameter[] parameters = producerMethod.getParameters();
+
+    Parameter intParam = parameters[1];
+    RequestPart intParamAnnotation = intParam.getAnnotation(RequestPart.class);
+    Assert.assertThat(requestPartAnnotationProcessor.getParameterName(intParamAnnotation),
+        Matchers.is("intParam"));
+  }
+
+  @Test
+  public void getHttpParameterType() {
+    Assert.assertThat(requestPartAnnotationProcessor.getHttpParameterType(null),
+        Matchers.is(HttpParameterType.FORM));
+  }
+
+  @Test
+  public void fillParameter_simpleType() {
+    Parameter param = producerMethod.getParameters()[0];
+    RequestPart requestPartAnnotation = param.getAnnotation(RequestPart.class);
+    FormParameter formParameter = new FormParameter();
+    requestPartAnnotationProcessor
+        .fillParameter(null, null, formParameter, param.getParameterizedType(), requestPartAnnotation);
+
+    Assert.assertThat(formParameter.getIn(), Matchers.is("formData"));
+    Assert.assertThat(formParameter.getType(), Matchers.is("string"));
+  }
+
+  @Test
+  public void fillParameter_simpleType_arrayPart() {
+    Parameter param = producerMethod.getParameters()[2];
+    RequestPart requestPartAnnotation = param.getAnnotation(RequestPart.class);
+    FormParameter formParameter = new FormParameter();
+    requestPartAnnotationProcessor
+        .fillParameter(null, null, formParameter, param.getParameterizedType(), requestPartAnnotation);
+
+    Assert.assertThat(formParameter.getIn(), Matchers.is("formData"));
+    Assert.assertThat(formParameter.getType(), Matchers.is("array"));
+    Assert.assertThat(formParameter.getItems(), Matchers.instanceOf(StringProperty.class));
+  }
+
+  @Test
+  public void fillParameter_simpleType_collectionPart() {
+    Parameter param = producerMethod.getParameters()[3];
+    RequestPart requestPartAnnotation = param.getAnnotation(RequestPart.class);
+    FormParameter formParameter = new FormParameter();
+    requestPartAnnotationProcessor
+        .fillParameter(null, null, formParameter, param.getParameterizedType(), requestPartAnnotation);
+
+    Assert.assertThat(formParameter.getIn(), Matchers.is("formData"));
+    Assert.assertThat(formParameter.getType(), Matchers.is("array"));
+    Assert.assertThat(formParameter.getItems(), Matchers.instanceOf(StringProperty.class));
+  }
+
+  @Test
+  public void fillParameter_uploadFile() {
+    Parameter param = producerMethod.getParameters()[4];
+    RequestPart requestPartAnnotation = param.getAnnotation(RequestPart.class);
+    FormParameter formParameter = new FormParameter();
+    requestPartAnnotationProcessor
+        .fillParameter(null, null, formParameter, param.getParameterizedType(), requestPartAnnotation);
+
+    Assert.assertThat(formParameter.getIn(), Matchers.is("formData"));
+    Assert.assertThat(formParameter.getType(), Matchers.is("file"));
+  }
+
+  @Test
+  public void fillParameter_uploadFile_arrayPart() {
+    Parameter param = producerMethod.getParameters()[5];
+    RequestPart requestPartAnnotation = param.getAnnotation(RequestPart.class);
+    FormParameter formParameter = new FormParameter();
+    requestPartAnnotationProcessor
+        .fillParameter(null, null, formParameter, param.getParameterizedType(), requestPartAnnotation);
+
+    Assert.assertThat(formParameter.getIn(), Matchers.is("formData"));
+    Assert.assertThat(formParameter.getType(), Matchers.is("array"));
+    Assert.assertThat(formParameter.getItems(), Matchers.instanceOf(FileProperty.class));
+  }
+
+  @Test
+  public void fillParameter_uploadFile_collectionPart() {
+    Parameter param = producerMethod.getParameters()[6];
+    RequestPart requestPartAnnotation = param.getAnnotation(RequestPart.class);
+    FormParameter formParameter = new FormParameter();
+    requestPartAnnotationProcessor
+        .fillParameter(null, null, formParameter, param.getParameterizedType(), requestPartAnnotation);
+
+    Assert.assertThat(formParameter.getIn(), Matchers.is("formData"));
+    Assert.assertThat(formParameter.getType(), Matchers.is("array"));
+    Assert.assertThat(formParameter.getItems(), Matchers.instanceOf(FileProperty.class));
+  }
+
+  public static class DemoRest {
+    public void fun(@RequestPart("stringParam") String stringParam,
+        @RequestPart(name = "intParam") int intParam,
+        @RequestPart("stringParamArray") String[] stringParamArray,
+        @RequestPart("stringParamCollection") List<String> stringParamCollection,
+        @RequestPart("file") MultipartFile file,
+        @RequestPart("fileArray") MultipartFile[] fileArray,
+        @RequestPart("fileCollection") List<MultipartFile> fileCollection) {
+    }
+  }
+}
\ No newline at end of file
diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreatorTest.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreatorTest.java
new file mode 100644
index 0000000..619e777
--- /dev/null
+++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreatorTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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.apache.servicecomb.swagger.generator.springmvc.property.creator;
+
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+import org.springframework.web.multipart.MultipartFile;
+
+import io.swagger.models.properties.FileProperty;
+import io.swagger.models.properties.Property;
+
+public class MultipartFilePropertyCreatorTest {
+  private MultipartFilePropertyCreator multipartFilePropertyCreator = new MultipartFilePropertyCreator();
+
+  @Test
+  public void createProperty() {
+    Property property = multipartFilePropertyCreator.createProperty();
+    Assert.assertThat(property, Matchers.instanceOf(FileProperty.class));
+  }
+
+  @Test
+  public void classes() {
+    Class<?>[] classes = multipartFilePropertyCreator.classes();
+    Assert.assertThat(classes, Matchers.arrayContaining(MultipartFile.class));
+  }
+}