You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by GitBox <gi...@apache.org> on 2019/01/16 10:00:07 UTC

[servicecomb-java-chassis] Diff for: [GitHub] liubao68 merged pull request #1028: [SCB-1046] file upload support file array for the same name

diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java
index a300273e8..9b360499e 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java
@@ -17,8 +17,6 @@
 
 package org.apache.servicecomb.common.rest.codec;
 
-import javax.servlet.http.Part;
-
 import io.vertx.core.MultiMap;
 import io.vertx.core.buffer.Buffer;
 
@@ -41,5 +39,5 @@
 
   Buffer getBodyBuffer() throws Exception;
 
-  void attach(String name, Part part);
+  void attach(String name, Object part);
 }
diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java
index 04ea5bfd6..6980e94d6 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java
@@ -18,7 +18,9 @@
 package org.apache.servicecomb.common.rest.codec.param;
 
 import java.lang.reflect.Type;
+import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.Part;
@@ -34,8 +36,10 @@
 import io.swagger.models.parameters.FormParameter;
 import io.swagger.models.parameters.Parameter;
 import io.swagger.models.properties.FileProperty;
+import io.swagger.models.properties.Property;
 
 public class FormProcessorCreator implements ParamValueProcessorCreator {
+
   public static final String PARAMTYPE = "formData";
 
   public static class FormProcessor extends AbstractParamProcessor {
@@ -99,22 +103,38 @@ public ParamValueProcessor create(Parameter parameter, Type genericParamType) {
   }
 
   private boolean isPart(Parameter parameter) {
-    return new FileProperty().getType().equals(((FormParameter) parameter).getType());
+    //only check
+    FormParameter formParameter = (FormParameter) parameter;
+    if ("array".equals(formParameter.getType())) {
+      Property items = formParameter.getItems();
+      return new FileProperty().getType().equals(items.getType());
+    }
+    return new FileProperty().getType().equals(formParameter.getType());
   }
 
-  private static class PartProcessor extends AbstractParamProcessor {
+  public static class PartProcessor extends AbstractParamProcessor {
     PartProcessor(String paramPath, JavaType targetType, Object defaultValue, boolean required) {
       super(paramPath, targetType, defaultValue, required);
     }
 
     @Override
     public Object getValue(HttpServletRequest request) throws Exception {
+      if (List.class.isAssignableFrom(targetType.getRawClass())) {
+        JavaType contentType = targetType.getContentType();
+        if (contentType != null && contentType.getRawClass().equals(Part.class)) {
+          //get all parts
+          return request.getParts()
+              .stream()
+              .filter(part -> part.getName().equals(paramPath))
+              .collect(Collectors.toList());
+        }
+      }
       return request.getPart(paramPath);
     }
 
     @Override
     public void setValue(RestClientRequest clientRequest, Object arg) throws Exception {
-      clientRequest.attach(paramPath, (Part) arg);
+      clientRequest.attach(paramPath, arg);
     }
 
     @Override
diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java
index 8b124ec8a..2bb92a96b 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java
@@ -24,6 +24,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.UUID;
@@ -39,6 +40,9 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+
 import io.vertx.core.Context;
 import io.vertx.core.MultiMap;
 import io.vertx.core.buffer.Buffer;
@@ -52,7 +56,7 @@
 
   protected AsyncResponse asyncResp;
 
-  private final Map<String, Part> uploads = new HashMap<>();
+  private final Multimap<String, Part> uploads = ArrayListMultimap.create();
 
   protected HttpClientRequest request;
 
@@ -80,12 +84,19 @@ public Buffer getBodyBuffer() throws Exception {
   }
 
   @Override
-  public void attach(String name, Part part) {
+  @SuppressWarnings("unchecked")
+  public void attach(String name, Object part) {
     if (null == part) {
       LOGGER.debug("null file is ignored, file name = [{}]", name);
       return;
     }
-    uploads.put(name, part);
+    if (List.class.isAssignableFrom(part.getClass())) {
+      List<Part> parts = (List<Part>) part;
+      uploads.putAll(name, parts);
+      return;
+    }
+    // must be part
+    uploads.put(name, (Part) part);
   }
 
   @Override
@@ -155,7 +166,7 @@ protected void doEndNormal() {
   }
 
   private void attachFiles(String boundary) {
-    Iterator<Entry<String, Part>> uploadsIterator = uploads.entrySet().iterator();
+    Iterator<Entry<String, Part>> uploadsIterator = uploads.entries().iterator();
     attachFile(boundary, uploadsIterator);
   }
 
@@ -172,7 +183,6 @@ private void attachFile(String boundary, Iterator<Entry<String, Part>> uploadsIt
     String name = entry.getKey();
     Part part = entry.getValue();
     String filename = part.getSubmittedFileName();
-
     Buffer fileHeader = fileBoundaryInfo(boundary, name, part);
     request.write(fileHeader);
 
diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java
index a417ef22c..2c2f2fca2 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java
@@ -30,6 +30,7 @@
 import javax.ws.rs.core.MediaType;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.servicecomb.common.rest.codec.param.FormProcessorCreator.PartProcessor;
 import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor;
 import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager;
 import org.apache.servicecomb.common.rest.definition.path.PathRegExp;
@@ -64,6 +65,8 @@
   // key为参数名
   protected Map<String, RestParam> paramMap = new LinkedHashMap<>();
 
+  protected List<String> fileKeys = new ArrayList<>();
+
   // key为数据类型,比如json之类
   private Map<String, ProduceProcessor> produceProcessorMap = new LinkedHashMap<>();
 
@@ -219,6 +222,9 @@ public URLPathBuilder getPathBuilder() {
   }
 
   private void addParam(RestParam param) {
+    if (param.getParamProcessor() instanceof PartProcessor) {
+      fileKeys.add(param.getParamName());
+    }
     paramList.add(param);
     paramMap.put(param.getParamName(), param);
   }
@@ -258,6 +264,10 @@ public String getHttpMethod() {
     return operationMeta.getHttpMethod();
   }
 
+  public List<String> getFileKeys() {
+    return fileKeys;
+  }
+
   public List<String> getProduces() {
     return produces;
   }
diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java
index 137063fd4..6bcb3f9ec 100644
--- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java
+++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java
@@ -16,6 +16,7 @@
  */
 package org.apache.servicecomb.common.rest.codec.param;
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
@@ -28,6 +29,8 @@
 import org.junit.Test;
 import org.mockito.Mockito;
 
+import com.google.common.collect.Multimap;
+
 import io.vertx.core.MultiMap;
 import io.vertx.core.buffer.Buffer;
 import io.vertx.core.http.CaseInsensitiveHeaders;
@@ -129,9 +132,9 @@ public void testAttach() {
 
     restClientRequest.attach(fileName, part);
 
-    Map<String, Part> uploads = Deencapsulation.getField(restClientRequest, "uploads");
+    Multimap<String, Part> uploads = Deencapsulation.getField(restClientRequest, "uploads");
     Assert.assertEquals(1, uploads.size());
-    Assert.assertThat(uploads, Matchers.hasEntry(fileName, part));
+    Assert.assertThat(uploads.asMap(), Matchers.hasEntry(fileName, Arrays.asList(part)));
   }
 
   @Test
@@ -140,7 +143,7 @@ public void testAttachOnPartIsNull() {
 
     restClientRequest.attach("fileName", null);
 
-    Map<String, Part> uploads = Deencapsulation.getField(restClientRequest, "uploads");
+    Multimap<String, Part> uploads = Deencapsulation.getField(restClientRequest, "uploads");
     Assert.assertTrue(uploads.isEmpty());
   }
 
diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java
index 9274bc270..d7dfdccfc 100644
--- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java
+++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java
@@ -17,6 +17,7 @@
 
 package org.apache.servicecomb.foundation.vertx.http;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -24,6 +25,7 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import javax.servlet.AsyncContext;
 import javax.servlet.ServletInputStream;
@@ -275,6 +277,12 @@ public Part getPart(String name) {
     return new FileUploadPart(fileUpload);
   }
 
+  @Override
+  public Collection<Part> getParts() {
+    Set<FileUpload> fileUploads = context.fileUploads();
+    return fileUploads.stream().map(FileUploadPart::new).collect(Collectors.toList());
+  }
+
   public RoutingContext getContext() {
     return context;
   }
diff --git a/integration-tests/it-common/src/main/resources/logback.xml b/integration-tests/it-common/src/main/resources/logback.xml
index d753514a0..4b5920888 100644
--- a/integration-tests/it-common/src/main/resources/logback.xml
+++ b/integration-tests/it-common/src/main/resources/logback.xml
@@ -23,6 +23,7 @@
       <pattern>%d [%level] [%thread] - %msg (%F:%L\)%n</pattern>
     </encoder>
   </appender>
+  <logger name="org.apache.servicecomb.common.javassist.JavassistUtils" level="WARN"/>
   <root level="INFO">
     <appender-ref ref="STDOUT"/>
   </root>
diff --git a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/ConsumerMain.java b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/ConsumerMain.java
index 94ffdb852..0289292e2 100644
--- a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/ConsumerMain.java
+++ b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/ConsumerMain.java
@@ -43,6 +43,7 @@
 import org.apache.servicecomb.it.testcase.TestRestServerConfigEdge;
 import org.apache.servicecomb.it.testcase.TestTrace;
 import org.apache.servicecomb.it.testcase.TestTraceEdge;
+import org.apache.servicecomb.it.testcase.TestUpload;
 import org.apache.servicecomb.it.testcase.base.TestGeneric;
 import org.apache.servicecomb.it.testcase.thirdparty.Test3rdPartyInvocation;
 
@@ -106,6 +107,7 @@ private static void runShareTestCases() throws Throwable {
     ITJUnitUtils.runWithRest(TestDefaultValue.class);
     ITJUnitUtils.runWithRest(TestAcceptType.class);
 
+    ITJUnitUtils.runWithRest(TestUpload.class);
     ITJUnitUtils.runWithRest(TestDownload.class);
     ITJUnitUtils.runWithHighwayAndRest(TestExceptionConvertEdge.class);
 
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
new file mode 100644
index 000000000..d3fd5c1d6
--- /dev/null
+++ b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestUpload.java
@@ -0,0 +1,344 @@
+/*
+ * 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.it.testcase;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+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.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+
+public class TestUpload {
+
+  private FileSystemResource fileSystemResource1;
+
+  private FileSystemResource fileSystemResource2;
+
+  private FileSystemResource fileSystemResource3;
+
+  private FileSystemResource fileSystemResource4;
+
+  private static final String message = "cseMessage";
+
+  interface UploadIntf {
+  }
+
+  private static Consumers<UploadIntf> consumersSpringmvc = new Consumers<>("uploadSpringmvcSchema",
+      UploadIntf.class);
+
+  private static Consumers<UploadIntf> consumersJaxrs = new Consumers<>("uploadJaxrsSchema",
+      UploadIntf.class);
+
+  @Before
+  public void init() {
+    try {
+      File file1 = File.createTempFile("jaxrstest1", ".txt");
+      File file2 = File.createTempFile("测试啊", ".txt");
+      File file3 = File.createTempFile("files", ".yaml");
+      File file4 = File.createTempFile("files4", ".yaml");
+      FileUtils.writeStringToFile(file1, "hello1");
+      FileUtils.writeStringToFile(file2, "中文 2");
+      FileUtils.writeStringToFile(file3, "cse3");
+      FileUtils.writeStringToFile(file4, "cse4");
+      fileSystemResource1 = new FileSystemResource(file1);
+      fileSystemResource2 = new FileSystemResource(file2);
+      fileSystemResource3 = new FileSystemResource(file3);
+      fileSystemResource4 = new FileSystemResource(file4);
+    } catch (IOException e) {
+      Assert.fail("Failed to create temp file");
+    }
+  }
+
+  @Test
+  public void testJarxUpload1() {
+    Map<String, Object> map = new HashMap<>();
+    map.put("file1", fileSystemResource1);
+    map.put("file2", fileSystemResource2);
+    String result = consumersJaxrs.getSCBRestTemplate().postForObject("/upload1", new HttpEntity<>(map), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "中文 2"));
+  }
+
+  @Test
+  public void testJarxUploadArray1() {
+    Map<String, Object> map = new HashMap<>();
+    FileSystemResource[] arrays = {fileSystemResource1, fileSystemResource2};
+    map.put("file1", arrays);
+    map.put("file2", fileSystemResource3);
+    String result = consumersJaxrs.getSCBRestTemplate()
+        .postForObject("/uploadArray1", new HttpEntity<>(map), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "中文 2", "cse3"));
+  }
+
+  @Test
+  public void testJarxUploadList1() {
+    Map<String, Object> map = new HashMap<>();
+    List<FileSystemResource> list = new ArrayList<>();
+    list.add(fileSystemResource1);
+    list.add(fileSystemResource2);
+    map.put("file1", list);
+    map.put("file2", fileSystemResource3);
+    String result = consumersJaxrs.getSCBRestTemplate()
+        .postForObject("/uploadList1", new HttpEntity<>(map), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "中文 2", "cse3"));
+  }
+
+  @Test
+  public void testJarxUploadArrayList1() {
+    Map<String, Object> map = new HashMap<>();
+    ArrayList<FileSystemResource> list = new ArrayList<>();
+    list.add(fileSystemResource1);
+    list.add(fileSystemResource2);
+    map.put("file1", list);
+    map.put("file2", fileSystemResource3);
+    String result = consumersJaxrs.getSCBRestTemplate()
+        .postForObject("/uploadArrayList1", new HttpEntity<>(map), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "中文 2", "cse3"));
+  }
+
+  @Test
+  public void testJarxUpload2() {
+    Map<String, Object> map = new HashMap<>();
+    map.put("file1", fileSystemResource1);
+    map.put("message", message);
+    String result = consumersJaxrs.getSCBRestTemplate()
+        .postForObject("/upload2", new HttpEntity<>(map), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", message));
+  }
+
+  @Test
+  public void testJarxUploadArray2() {
+    Map<String, Object> map = new HashMap<>();
+    FileSystemResource[] arrays = {fileSystemResource1, fileSystemResource2};
+    map.put("file1", arrays);
+    map.put("message", message);
+    String result = consumersJaxrs.getSCBRestTemplate()
+        .postForObject("/uploadArray2", new HttpEntity<>(map), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "中文 2", message));
+  }
+
+  @Test
+  public void testJarxUploadList2() {
+    Map<String, Object> map = new HashMap<>();
+    List<FileSystemResource> list = new ArrayList<>();
+    list.add(fileSystemResource2);
+    list.add(fileSystemResource3);
+    map.put("file1", list);
+    map.put("message", message);
+    String result = consumersJaxrs.getSCBRestTemplate()
+        .postForObject("/uploadList2", new HttpEntity<>(map), String.class);
+    Assert.assertTrue(containsAll(result, "cse3", "中文 2", message));
+  }
+
+  @Test
+  public void testJarxUploadArrayList2() {
+    Map<String, Object> map = new HashMap<>();
+    ArrayList<FileSystemResource> list = new ArrayList<>();
+    list.add(fileSystemResource2);
+    list.add(fileSystemResource3);
+    map.put("file1", list);
+    map.put("message", message);
+    String result = consumersJaxrs.getSCBRestTemplate()
+        .postForObject("/uploadArrayList2", new HttpEntity<>(map), String.class);
+    Assert.assertTrue(containsAll(result, "cse3", "中文 2", message));
+  }
+
+  @Test
+  public void testJarxUploadMix() {
+    Map<String, Object> map = new HashMap<>();
+    List<FileSystemResource> list = new ArrayList<>();
+    list.add(fileSystemResource4);
+    list.add(fileSystemResource3);
+    FileSystemResource[] arrays = {fileSystemResource1, fileSystemResource2};
+    map.put("file1", list);
+    map.put("file2", arrays);
+    map.put("message", message);
+    String result = consumersJaxrs.getSCBRestTemplate()
+        .postForObject("/uploadMix", new HttpEntity<>(map), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "cse4", "cse3", "中文 2", message));
+  }
+
+  //springmvc
+  @Test
+  public void testFileUpload() {
+    Map<String, Object> map = new HashMap<>();
+    map.put("file1", fileSystemResource1);
+    map.put("file2", fileSystemResource2);
+    map.put("name", message);
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+    String result = consumersSpringmvc.getSCBRestTemplate().postForObject("/upload",
+        new HttpEntity<>(map, headers), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "中文 2", message));
+  }
+
+  @Test
+  public void testFileUploadArray() {
+    Map<String, Object> map = new HashMap<>();
+    FileSystemResource[] array1 = {fileSystemResource1, fileSystemResource2};
+    FileSystemResource[] array2 = {fileSystemResource3, fileSystemResource4};
+    map.put("file1", array1);
+    map.put("file2", array2);
+    map.put("name", message);
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+    String result = consumersSpringmvc.getSCBRestTemplate()
+        .postForObject("/uploadArray", new HttpEntity<>(map, headers), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "cse4", "cse3", "中文 2", message));
+  }
+
+  @Test
+  public void testFileUploadList() {
+    Map<String, Object> map = new HashMap<>();
+    List<FileSystemResource> list1 = new ArrayList<>();
+    List<FileSystemResource> list2 = new ArrayList<>();
+    list1.add(fileSystemResource1);
+    list1.add(fileSystemResource2);
+    list2.add(fileSystemResource3);
+    list2.add(fileSystemResource4);
+    map.put("file1", list1);
+    map.put("file2", list2);
+    map.put("name", message);
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+    String result = consumersSpringmvc.getSCBRestTemplate()
+        .postForObject("/uploadList", new HttpEntity<>(map, headers), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "cse4", "cse3", "中文 2", message));
+  }
+
+  @Test
+  public void testFileUploadArrayList() {
+    Map<String, Object> map = new HashMap<>();
+    ArrayList<FileSystemResource> list1 = new ArrayList<>();
+    ArrayList<FileSystemResource> list2 = new ArrayList<>();
+    list1.add(fileSystemResource1);
+    list1.add(fileSystemResource2);
+    list2.add(fileSystemResource3);
+    list2.add(fileSystemResource4);
+    map.put("file1", list1);
+    map.put("file2", list2);
+    map.put("name", message);
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+    String result = consumersSpringmvc.getSCBRestTemplate()
+        .postForObject("/uploadArrayList", new HttpEntity<>(map, headers), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "cse4", "cse3", "中文 2", message));
+  }
+
+  @Test
+  public void testFileUploadWithoutAnnotation() {
+    Map<String, Object> map = new HashMap<>();
+    map.put("file1", fileSystemResource1);
+    map.put("file2", fileSystemResource2);
+    map.put("name", message);
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+    String result = consumersSpringmvc.getSCBRestTemplate().postForObject("/uploadWithoutAnnotation",
+        new HttpEntity<>(map, headers), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "中文 2", message));
+  }
+
+  @Test
+  public void testFileUploadArrayWithoutAnnotation() {
+    Map<String, Object> map = new HashMap<>();
+    FileSystemResource[] array1 = {fileSystemResource1, fileSystemResource2};
+    FileSystemResource[] array2 = {fileSystemResource3, fileSystemResource4};
+    map.put("file1", array1);
+    map.put("file2", array2);
+    map.put("name", message);
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+    String result = consumersSpringmvc.getSCBRestTemplate()
+        .postForObject("/uploadArrayWithoutAnnotation", new HttpEntity<>(map, headers), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "cse4", "cse3", "中文 2", message));
+  }
+
+  @Test
+  public void testFileUploadListWithoutAnnotation() {
+    Map<String, Object> map = new HashMap<>();
+    List<FileSystemResource> list1 = new ArrayList<>();
+    List<FileSystemResource> list2 = new ArrayList<>();
+    list1.add(fileSystemResource1);
+    list1.add(fileSystemResource2);
+    list2.add(fileSystemResource3);
+    list2.add(fileSystemResource4);
+    map.put("file1", list1);
+    map.put("file2", list2);
+    map.put("name", message);
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+    String result = consumersSpringmvc.getSCBRestTemplate()
+        .postForObject("/uploadListArrayWithoutAnnotation", new HttpEntity<>(map, headers), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "cse4", "cse3", "中文 2", message));
+  }
+
+  @Test
+  public void testFileUploadArrayListWithoutAnnotation() {
+    Map<String, Object> map = new HashMap<>();
+    ArrayList<FileSystemResource> list1 = new ArrayList<>();
+    ArrayList<FileSystemResource> list2 = new ArrayList<>();
+    list1.add(fileSystemResource1);
+    list1.add(fileSystemResource2);
+    list2.add(fileSystemResource3);
+    list2.add(fileSystemResource4);
+    map.put("file1", list1);
+    map.put("file2", list2);
+    map.put("name", message);
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+    String result = consumersSpringmvc.getSCBRestTemplate()
+        .postForObject("/uploadArrayListArrayWithoutAnnotation", new HttpEntity<>(map, headers), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "cse4", "cse3", "中文 2", message));
+  }
+
+  @Test
+  public void testFileUploadMixWithoutAnnotation() {
+    Map<String, Object> map = new HashMap<>();
+    List<FileSystemResource> list1 = new ArrayList<>();
+    list1.add(fileSystemResource1);
+    list1.add(fileSystemResource2);
+    FileSystemResource[] array2 = {fileSystemResource3, fileSystemResource4};
+    map.put("file1", list1);
+    map.put("file2", array2);
+    map.put("name", message);
+    HttpHeaders headers = new HttpHeaders();
+    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+    String result = consumersSpringmvc.getSCBRestTemplate()
+        .postForObject("/uploadMix", new HttpEntity<>(map, headers), String.class);
+    Assert.assertTrue(containsAll(result, "hello1", "cse4", "cse3", "中文 2", message));
+  }
+
+
+  private static boolean containsAll(String str, String... strings) {
+    for (String string : strings) {
+      if (!str.contains(string)) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
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
new file mode 100644
index 000000000..6dccf710b
--- /dev/null
+++ b/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/UploadJaxrsSchema.java
@@ -0,0 +1,146 @@
+/*
+ * 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.it.schema;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.Part;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+
+@RestSchema(schemaId = "uploadJaxrsSchema")
+@Path("/v1/uploadJaxrsSchema")
+public class UploadJaxrsSchema {
+  @Path("/upload1")
+  @POST
+  @Produces(MediaType.TEXT_PLAIN)
+  public String fileUpload1(@FormParam("file1") Part file1, @FormParam("file2") Part file2) throws IOException {
+    return getStrFromPart(file1) + getStrFromPart(file2);
+  }
+
+  @Path("/uploadArray1")
+  @POST
+  @Produces(MediaType.TEXT_PLAIN)
+  public String uploadArray1(@FormParam("file1") Part[] file1, @FormParam("file2") Part file2) throws IOException {
+    StringBuilder stringBuilder = new StringBuilder();
+    for (int i = 0; i < file1.length; i++) {
+      stringBuilder.append(getStrFromPart(file1[i]));
+    }
+    return stringBuilder.append(getStrFromPart(file2)).toString();
+  }
+
+
+  @Path("/uploadList1")
+  @POST
+  @Produces(MediaType.TEXT_PLAIN)
+  public String uploadList1(@FormParam("file1") List<Part> file1, @FormParam("file2") Part file2)
+      throws IOException {
+    StringBuilder stringBuilder = new StringBuilder();
+    file1.forEach(part -> {
+      stringBuilder.append(getStrFromPart(part));
+    });
+    return stringBuilder.append(getStrFromPart(file2)).toString();
+  }
+
+  @Path("/uploadArrayList1")
+  @POST
+  @Produces(MediaType.TEXT_PLAIN)
+  public String uploadArrayList1(@FormParam("file1") ArrayList<Part> file1, @FormParam("file2") Part file2)
+      throws IOException {
+    StringBuilder stringBuilder = new StringBuilder();
+    file1.forEach(part -> {
+      stringBuilder.append(getStrFromPart(part));
+    });
+    return stringBuilder.append(getStrFromPart(file2)).toString();
+  }
+
+
+  @Path("/upload2")
+  @POST
+  @Produces(MediaType.TEXT_PLAIN)
+  public String fileUpload2(@FormParam("file1") Part file1, @FormParam("message") String message) throws IOException {
+    return getStrFromPart(file1) + message;
+  }
+
+  @Path("/uploadArray2")
+  @POST
+  @Produces(MediaType.TEXT_PLAIN)
+  public String uploadArray2(@FormParam("file1") Part[] file1, @FormParam("message") String message)
+      throws IOException {
+    StringBuilder stringBuilder = new StringBuilder();
+    for (int i = 0; i < file1.length; i++) {
+      stringBuilder.append(getStrFromPart(file1[i]));
+    }
+    return stringBuilder.append(message).toString();
+  }
+
+
+  @Path("/uploadList2")
+  @POST
+  @Produces(MediaType.TEXT_PLAIN)
+  public String uploadList2(@FormParam("file1") List<Part> file1, @FormParam("message") String message) {
+    StringBuilder stringBuilder = new StringBuilder();
+    file1.forEach(part -> {
+      stringBuilder.append(getStrFromPart(part));
+    });
+    return stringBuilder.append(message).toString();
+  }
+
+  @Path("/uploadArrayList2")
+  @POST
+  @Produces(MediaType.TEXT_PLAIN)
+  public String uploadArrayList2(@FormParam("file1") ArrayList<Part> file1, @FormParam("message") String message) {
+    StringBuilder stringBuilder = new StringBuilder();
+    file1.forEach(part -> {
+      stringBuilder.append(getStrFromPart(part));
+    });
+    return stringBuilder.append(message).toString();
+  }
+
+  @Path("/uploadMix")
+  @POST
+  @Produces(MediaType.TEXT_PLAIN)
+  public String uploadMix(@FormParam("file1") List<Part> file1, @FormParam("file2") Part[] file2,
+      @FormParam("message") String message) {
+    StringBuilder stringBuilder = new StringBuilder();
+    file1.forEach(part -> {
+      stringBuilder.append(getStrFromPart(part));
+    });
+    for (int i = 0; i < file2.length; i++) {
+      stringBuilder.append(getStrFromPart(file2[i]));
+    }
+    return stringBuilder.append(message).toString();
+  }
+
+  private static String getStrFromPart(Part file1) {
+    try (InputStream is1 = file1.getInputStream()) {
+      return IOUtils.toString(is1, "utf-8");
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    return "";
+  }
+}
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
new file mode 100644
index 000000000..d77570c7a
--- /dev/null
+++ b/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/UploadSpringmvcSchema.java
@@ -0,0 +1,118 @@
+/*
+ * 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.it.schema;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.RequestAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.google.common.collect.Lists;
+
+
+@RestSchema(schemaId = "uploadSpringmvcSchema")
+@RequestMapping(path = "/v1/uploadSpringmvcSchema")
+public class UploadSpringmvcSchema {
+
+  @RequestMapping(path = "/upload", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+  public String fileUpload(@RequestPart(name = "file1") MultipartFile file1,
+      @RequestPart(name = "file2") MultipartFile file2, @RequestAttribute("name") String name) {
+    return _fileUpload(Lists.newArrayList(file1, file2)) + name;
+  }
+
+  @RequestMapping(path = "/uploadArray", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+  public String fileUploadArray(@RequestPart(name = "file1") MultipartFile[] file1,
+      @RequestPart(name = "file2") MultipartFile[] file2, @RequestAttribute("name") String name) {
+    List<MultipartFile> multipartFileList = new ArrayList<>();
+    multipartFileList.addAll(Arrays.asList(file1));
+    multipartFileList.addAll(Arrays.asList(file2));
+    return _fileUpload(multipartFileList) + name;
+  }
+
+  @RequestMapping(path = "/uploadList", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+  public String fileUploadList(@RequestPart(name = "file1") List<MultipartFile> file1,
+      @RequestPart(name = "file2") List<MultipartFile> file2, @RequestAttribute("name") String name) {
+    file1.addAll(file2);
+    return _fileUpload(file1) + name;
+  }
+
+  @RequestMapping(path = "/uploadArrayList", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+  public String fileUploadArrayList(@RequestPart(name = "file1") ArrayList<MultipartFile> file1,
+      @RequestPart(name = "file2") ArrayList<MultipartFile> file2, @RequestAttribute("name") String name) {
+    file1.addAll(file2);
+    return _fileUpload(file1) + name;
+  }
+
+  @RequestMapping(path = "/uploadWithoutAnnotation", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+  public String uploadWithoutAnnotation(MultipartFile file1, MultipartFile file2,
+      @RequestAttribute("name") String name) {
+    return _fileUpload(Lists.newArrayList(file1, file2)) + name;
+  }
+
+  @RequestMapping(path = "/uploadArrayWithoutAnnotation", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+  public String uploadArrayWithoutAnnotation(MultipartFile[] file1, MultipartFile[] file2,
+      @RequestAttribute("name") String name) {
+    List<MultipartFile> multipartFileList = new ArrayList<>();
+    multipartFileList.addAll(Arrays.asList(file1));
+    multipartFileList.addAll(Arrays.asList(file2));
+    return _fileUpload(multipartFileList) + name;
+  }
+
+  @RequestMapping(path = "/uploadListArrayWithoutAnnotation", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+  public String uploadListWithoutAnnotation(List<MultipartFile> file1, List<MultipartFile> file2,
+      @RequestAttribute("name") String name) {
+    file1.addAll(file2);
+    return _fileUpload(file1) + name;
+  }
+
+  @RequestMapping(path = "/uploadArrayListArrayWithoutAnnotation", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+  public String uploadArrayListWithoutAnnotation(ArrayList<MultipartFile> file1, ArrayList<MultipartFile> file2,
+      @RequestAttribute("name") String name) {
+    file1.addAll(file2);
+    return _fileUpload(file1) + name;
+  }
+
+  @RequestMapping(path = "/uploadMix", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+  public String uploadMix(@RequestPart(name = "file1") List<MultipartFile> file1,
+      @RequestPart(name = "file2") MultipartFile[] file2, @RequestAttribute("name") String name) {
+    List<MultipartFile> multipartFileList = Arrays.asList(file2);
+    file1.addAll(multipartFileList);
+    return _fileUpload(file1) + name;
+  }
+
+  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/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CommonToHttpServletRequest.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CommonToHttpServletRequest.java
index 2d5d75ed8..5bfe5e3f5 100644
--- a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CommonToHttpServletRequest.java
+++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CommonToHttpServletRequest.java
@@ -29,7 +29,6 @@
 import java.util.List;
 import java.util.Map;
 
-import javax.servlet.ServletException;
 import javax.servlet.ServletInputStream;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.Part;
@@ -42,20 +41,25 @@
 import org.apache.servicecomb.foundation.vertx.http.AbstractHttpServletRequest;
 import org.springframework.core.io.Resource;
 
+import com.google.common.annotations.VisibleForTesting;
+
 // restTemplate convert parameters to invocation args.
 public class CommonToHttpServletRequest extends AbstractHttpServletRequest {
   private Map<String, List<String>> queryParams;
 
   private Map<String, List<String>> httpHeaders;
 
+  //contains all the file key in the parts
+  private List<String> fileKeys = new ArrayList<>();
+
   // gen by httpHeaders
   private Cookie[] cookies;
 
   @SuppressWarnings("unchecked")
   public CommonToHttpServletRequest(Map<String, String> pathParams, Map<String, List<String>> queryParams,
-      Map<String, List<String>> httpHeaders, Object bodyObject, boolean isFormData) {
+      Map<String, List<String>> httpHeaders, Object bodyObject, boolean isFormData, List<String> fileKeys) {
     setAttribute(RestConst.PATH_PARAMETERS, pathParams);
-
+    this.fileKeys = fileKeys;
     if (isFormData) {
       setAttribute(RestConst.FORM_PARAMETERS, (Map<String, Object>) bodyObject);
     } else {
@@ -66,6 +70,12 @@ public CommonToHttpServletRequest(Map<String, String> pathParams, Map<String, Li
     this.httpHeaders = httpHeaders;
   }
 
+  @SuppressWarnings("unchecked")
+  public CommonToHttpServletRequest(Map<String, String> pathParams, Map<String, List<String>> queryParams,
+      Map<String, List<String>> httpHeaders, Object bodyObject, boolean isFormData) {
+    this(pathParams, queryParams, httpHeaders, bodyObject, isFormData, null);
+  }
+
   @Override
   public String getContentType() {
     return getHeader(HttpHeaders.CONTENT_TYPE);
@@ -164,8 +174,12 @@ public void addHeader(String name, String value) {
   }
 
   @Override
-  public Part getPart(String name) throws IOException, ServletException {
+  public Part getPart(String name) {
     Object value = findPartInputValue(name);
+    return getSinglePart(name, value);
+  }
+
+  private Part getSinglePart(String name, Object value) {
     if (value == null) {
       return null;
     }
@@ -196,6 +210,39 @@ public Part getPart(String name) throws IOException, ServletException {
             value.getClass().getName()));
   }
 
+  @Override
+  public Collection<Part> getParts() {
+    @SuppressWarnings("unchecked")
+    Map<String, Object> form = (Map<String, Object>) getAttribute(RestConst.FORM_PARAMETERS);
+    List<Part> partList = new ArrayList<>();
+    filePartListWithForm(partList, form);
+    return partList;
+  }
+
+  private void filePartListWithForm(List<Part> partList, Map<String, Object> form) {
+    for (String key : fileKeys) {
+      Object value = form.get(key);
+      if (value == null) {
+        continue;
+      }
+      if (Collection.class.isInstance(value)) {
+        Collection<?> collection = (Collection<?>) value;
+        for (Object part : collection) {
+          partList.add(getSinglePart(key, part));
+        }
+        continue;
+      }
+      if (value.getClass().isArray()) {
+        Object[] params = (Object[]) value;
+        for (int i = 0; i < params.length; i++) {
+          partList.add(getSinglePart(key, params[i]));
+        }
+        continue;
+      }
+      partList.add(getSinglePart(key, value));
+    }
+  }
+
   protected Object findPartInputValue(String name) {
     @SuppressWarnings("unchecked")
     Map<String, Object> form = (Map<String, Object>) getAttribute(RestConst.FORM_PARAMETERS);
@@ -214,4 +261,9 @@ protected Object findPartInputValue(String name) {
     }
     return value;
   }
+
+  @VisibleForTesting
+  public List<String> getFileKeys() {
+    return fileKeys;
+  }
 }
diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java
index 6f3388396..119139171 100644
--- a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java
+++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java
@@ -206,7 +206,8 @@ protected Response doInvoke(Invocation invocation) {
 
   protected Object[] collectArguments() {
     HttpServletRequest mockRequest = new CommonToHttpServletRequest(requestMeta.getPathParams(), queryParams,
-        httpHeaders, requestBody, requestMeta.getSwaggerRestOperation().isFormData());
+        httpHeaders, requestBody, requestMeta.getSwaggerRestOperation().isFormData(),
+        requestMeta.getSwaggerRestOperation().getFileKeys());
     return RestCodec.restToArgs(mockRequest, requestMeta.getSwaggerRestOperation());
   }
 }
diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCommonToHttpServletRequest.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCommonToHttpServletRequest.java
index d0522cd77..6461e6aed 100644
--- a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCommonToHttpServletRequest.java
+++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCommonToHttpServletRequest.java
@@ -17,8 +17,11 @@
 
 package org.apache.servicecomb.provider.springmvc.reference;
 
+import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -26,6 +29,7 @@
 
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.Part;
 import javax.ws.rs.core.HttpHeaders;
 
 import org.apache.servicecomb.common.rest.RestConst;
@@ -53,6 +57,17 @@ public void testConstructFormFalse() {
     Assert.assertEquals(body, request.getAttribute(RestConst.BODY_PARAMETER));
   }
 
+  @Test
+  public void testConstructNormal() {
+    List<String> fileKeys = new ArrayList<>();
+    fileKeys.add("test1");
+    fileKeys.add("test2");
+    HttpServletRequest request = new CommonToHttpServletRequest(null, null, null, null, false, fileKeys);
+    Assert.assertEquals(2, ((CommonToHttpServletRequest) request).getFileKeys().size());
+    Assert.assertEquals("test1", ((CommonToHttpServletRequest) request).getFileKeys().get(0));
+    Assert.assertEquals("test2", ((CommonToHttpServletRequest) request).getFileKeys().get(1));
+  }
+
   @Test
   public void testConstructPath() {
     Map<String, String> pathParams = new HashMap<>();
@@ -235,4 +250,34 @@ public void testAddHeader() {
     request.addHeader("name", "v2");
     Assert.assertThat(Collections.list(request.getHeaders("name")), Matchers.contains("v1", "v2"));
   }
+
+  @Test
+  public void testGetParts() {
+    List<String> restParams = new ArrayList<>();
+    restParams.add("test1");
+    restParams.add("test2");
+    File file1 = new File("file1.txt");
+    File file2 = new File("file2.txt");
+    File[] files = {file1, file2};
+    List<File> list = Arrays.asList(files);
+    Map<String, Object> objectMap = new HashMap<>();
+    objectMap.put("test1", list);
+    objectMap.put("test2", files);
+    objectMap.put("test3", list);
+    objectMap.put("test4", "haha");
+
+    Map<String, String> pathParams = new HashMap<>();
+    HttpServletRequest request = new CommonToHttpServletRequest(pathParams, null, null, objectMap, true, restParams);
+    try {
+      Collection<Part> tmpParts = request.getParts();
+      ArrayList<Part> parts = new ArrayList<>(tmpParts);
+      Assert.assertEquals(4, parts.size());
+      Assert.assertEquals("test1", parts.get(0).getName());
+      Assert.assertEquals("test1", parts.get(1).getName());
+      Assert.assertEquals("test2", parts.get(2).getName());
+      Assert.assertEquals("test2", parts.get(3).getName());
+    } catch (Throwable e) {
+      Assert.fail("should not throw exception");
+    }
+  }
 }
diff --git a/swagger/swagger-generator/generator-core/pom.xml b/swagger/swagger-generator/generator-core/pom.xml
index fd22a22f4..924467b3c 100644
--- a/swagger/swagger-generator/generator-core/pom.xml
+++ b/swagger/swagger-generator/generator-core/pom.xml
@@ -65,6 +65,10 @@
       <artifactId>log4j</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.google.inject</groupId>
+      <artifactId>guice</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.servicecomb</groupId>
       <artifactId>foundation-test-scaffolding</artifactId>
diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/CommonParameterTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/CommonParameterTypeProcessor.java
index 789cdcaa5..1b32f274a 100644
--- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/CommonParameterTypeProcessor.java
+++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/CommonParameterTypeProcessor.java
@@ -17,9 +17,12 @@
 
 package org.apache.servicecomb.swagger.generator.core;
 
+
+import java.lang.reflect.Type;
+
 /**
  * 通过spi自动注入到所有的GeneratorContext实例中去
  */
 public interface CommonParameterTypeProcessor extends ParameterTypeProcessor {
-  Class<?> getParameterType();
+  Type getParameterType();
 }
diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/OperationGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/OperationGenerator.java
index 8c9a83429..feacd86dc 100644
--- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/OperationGenerator.java
+++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/OperationGenerator.java
@@ -19,6 +19,7 @@
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -37,6 +38,8 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.util.StringUtils;
 
+import com.google.inject.util.Types;
+
 import io.swagger.models.HttpMethod;
 import io.swagger.models.Operation;
 import io.swagger.models.Path;
@@ -344,11 +347,30 @@ protected void processByParameterAnnotation(Annotation[] paramAnnotations, int p
 
   protected void processByParameterType(Type parameterType, int paramIdx) {
     ParameterTypeProcessor processor = context.findParameterTypeProcessor(parameterType);
+    if (processor == null) {
+      //maybe is ArrayList ...
+      Type realType = checkAndGetType(parameterType);
+      if (realType != null) {
+        processor = context.findParameterTypeProcessor(realType);
+      }
+    }
     if (processor != null) {
       processor.process(this, paramIdx);
     }
   }
 
+  // check whether is ArrayList , LinkedArrayList ...  or not
+  private Type checkAndGetType(Type type) {
+    if (ParameterizedType.class.isAssignableFrom(type.getClass())) {
+      ParameterizedType targetType = (ParameterizedType) type;
+      Class<?> targetCls = (Class<?>) targetType.getRawType();
+      if (List.class.isAssignableFrom(targetCls)) {
+        return Types.newParameterizedType(List.class, (Class<?>) targetType.getActualTypeArguments()[0]);
+      }
+    }
+    return null;
+  }
+
   public void correctOperation() {
     if (StringUtils.isEmpty(operation.getOperationId())) {
       operation.setOperationId(providerMethod.getName());
diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parametertype/HttpServletRequestProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parametertype/HttpServletRequestProcessor.java
index 3b5bb0dc1..731d2db0e 100644
--- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parametertype/HttpServletRequestProcessor.java
+++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parametertype/HttpServletRequestProcessor.java
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.swagger.generator.core.processor.parametertype;
 
+import java.lang.reflect.Type;
+
 import javax.servlet.http.HttpServletRequest;
 
 import org.apache.servicecomb.swagger.extend.parameter.HttpRequestParameter;
@@ -25,7 +27,7 @@
 
 public class HttpServletRequestProcessor implements CommonParameterTypeProcessor {
   @Override
-  public Class<?> getParameterType() {
+  public Type getParameterType() {
     return HttpServletRequest.class;
   }
 
diff --git a/swagger/swagger-generator/generator-springmvc/pom.xml b/swagger/swagger-generator/generator-springmvc/pom.xml
index 94b59394f..5e8b4f679 100644
--- a/swagger/swagger-generator/generator-springmvc/pom.xml
+++ b/swagger/swagger-generator/generator-springmvc/pom.xml
@@ -45,6 +45,10 @@
       <artifactId>log4j</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.google.inject</groupId>
+      <artifactId>guice</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.servicecomb</groupId>
       <artifactId>foundation-test-scaffolding</artifactId>
diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileArrayTypeProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileArrayTypeProcessor.java
new file mode 100644
index 000000000..4b040c0ba
--- /dev/null
+++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileArrayTypeProcessor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.parameter;
+
+import java.lang.reflect.Type;
+
+import org.apache.servicecomb.swagger.generator.core.CommonParameterTypeProcessor;
+import org.apache.servicecomb.swagger.generator.core.OperationGenerator;
+import org.apache.servicecomb.swagger.generator.core.utils.ParamUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+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 MultipartFileArrayTypeProcessor implements CommonParameterTypeProcessor {
+  @Override
+  public Type getParameterType() {
+    return MultipartFile[].class;
+  }
+
+  @Override
+  public void process(OperationGenerator operationGenerator, int paramIdx) {
+    FormParameter parameter = new FormParameter();
+    parameter.setName(ParamUtils.getParameterName(operationGenerator.getProviderMethod(), paramIdx));
+    Property property = new ArrayProperty(new FileProperty());
+    parameter.setProperty(property);
+    operationGenerator.addProviderParameter(parameter);
+  }
+}
diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileListTypeProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileListTypeProcessor.java
new file mode 100644
index 000000000..289ecddf8
--- /dev/null
+++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileListTypeProcessor.java
@@ -0,0 +1,50 @@
+/*
+ * 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.parameter;
+
+import java.lang.reflect.Type;
+import java.util.List;
+
+import org.apache.servicecomb.swagger.generator.core.CommonParameterTypeProcessor;
+import org.apache.servicecomb.swagger.generator.core.OperationGenerator;
+import org.apache.servicecomb.swagger.generator.core.utils.ParamUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.google.inject.util.Types;
+
+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 MultipartFileListTypeProcessor implements CommonParameterTypeProcessor {
+
+  @Override
+  public Type getParameterType() {
+    return Types.newParameterizedType(List.class, MultipartFile.class);
+  }
+
+  @Override
+  public void process(OperationGenerator operationGenerator, int paramIdx) {
+    FormParameter parameter = new FormParameter();
+    parameter.setName(ParamUtils.getParameterName(operationGenerator.getProviderMethod(), paramIdx));
+    Property property = new ArrayProperty(new FileProperty());
+    parameter.setProperty(property);
+    operationGenerator.addProviderParameter(parameter);
+  }
+}
diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileTypeProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileTypeProcessor.java
index 12e4974b5..8d0972d0d 100644
--- a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileTypeProcessor.java
+++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileTypeProcessor.java
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.swagger.generator.springmvc.processor.parameter;
 
+import java.lang.reflect.Type;
+
 import org.apache.servicecomb.swagger.generator.core.CommonParameterTypeProcessor;
 import org.apache.servicecomb.swagger.generator.core.OperationGenerator;
 import org.apache.servicecomb.swagger.generator.core.utils.ParamUtils;
@@ -27,7 +29,7 @@
 
 public class MultipartFileTypeProcessor implements CommonParameterTypeProcessor {
   @Override
-  public Class<?> getParameterType() {
+  public Type getParameterType() {
     return MultipartFile.class;
   }
 
diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.core.CommonParameterTypeProcessor b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.core.CommonParameterTypeProcessor
index 0bad0b7e0..bea33a134 100644
--- a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.core.CommonParameterTypeProcessor
+++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.core.CommonParameterTypeProcessor
@@ -16,3 +16,5 @@
 #
 
 org.apache.servicecomb.swagger.generator.springmvc.processor.parameter.MultipartFileTypeProcessor
+org.apache.servicecomb.swagger.generator.springmvc.processor.parameter.MultipartFileArrayTypeProcessor
+org.apache.servicecomb.swagger.generator.springmvc.processor.parameter.MultipartFileListTypeProcessor
\ No newline at end of file
diff --git a/swagger/swagger-invocation/invocation-core/pom.xml b/swagger/swagger-invocation/invocation-core/pom.xml
index 3dd32e48e..34b06c8f4 100644
--- a/swagger/swagger-invocation/invocation-core/pom.xml
+++ b/swagger/swagger-invocation/invocation-core/pom.xml
@@ -28,6 +28,10 @@
 
 
   <dependencies>
+    <dependency>
+      <groupId>com.google.inject</groupId>
+      <artifactId>guice</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.servicecomb</groupId>
       <artifactId>swagger-generator-core</artifactId>
diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/ConverterMgr.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/ConverterMgr.java
index 94b8570d0..faf65ecb4 100644
--- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/ConverterMgr.java
+++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/ConverterMgr.java
@@ -37,6 +37,8 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import com.google.inject.util.Types;
+
 @Component
 public class ConverterMgr {
   private static final Logger LOGGER = LoggerFactory.getLogger(ConverterMgr.class);
@@ -99,13 +101,33 @@ public Converter findConverter(Type src, Type target) {
     return findCommonConverter(target);
   }
 
+
   protected Converter findSrcTarget(Type src, Type target) {
     Map<Type, Converter> map = srcTargetMap.get(src);
-    if (map == null) {
-      return null;
+    if (map != null) {
+      Converter converter = map.get(target);
+      if (converter == null) {
+        //maybe target class is ArrayList...
+        Type realTarget = checkAndGetType(target);
+        if (realTarget != null) {
+          converter = map.get(realTarget);
+        }
+      }
+      return converter;
     }
+    return null;
+  }
 
-    return map.get(target);
+  // check whether is ArrayList , LinkedArrayList ...  or not
+  private Type checkAndGetType(Type type) {
+    if (ParameterizedType.class.isAssignableFrom(type.getClass())) {
+      ParameterizedType targetType = (ParameterizedType) type;
+      Class<?> targetCls = (Class<?>) targetType.getRawType();
+      if (List.class.isAssignableFrom(targetCls)) {
+        return Types.newParameterizedType(List.class, (Class<?>) targetType.getActualTypeArguments()[0]);
+      }
+    }
+    return null;
   }
 
   protected Converter findCommonConverter(Type target) {
diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/generator/InvocationContextProcessor.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/generator/InvocationContextProcessor.java
index c995a9330..5b4467ca5 100644
--- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/generator/InvocationContextProcessor.java
+++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/generator/InvocationContextProcessor.java
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.swagger.invocation.generator;
 
+import java.lang.reflect.Type;
+
 import org.apache.servicecomb.swagger.extend.parameter.InvocationContextParameter;
 import org.apache.servicecomb.swagger.generator.core.CommonParameterTypeProcessor;
 import org.apache.servicecomb.swagger.generator.core.OperationGenerator;
@@ -24,7 +26,7 @@
 
 public class InvocationContextProcessor implements CommonParameterTypeProcessor {
   @Override
-  public Class<?> getParameterType() {
+  public Type getParameterType() {
     return InvocationContext.class;
   }
 
diff --git a/swagger/swagger-invocation/invocation-springmvc/pom.xml b/swagger/swagger-invocation/invocation-springmvc/pom.xml
index 6cd333dbc..bd9fc5b2b 100644
--- a/swagger/swagger-invocation/invocation-springmvc/pom.xml
+++ b/swagger/swagger-invocation/invocation-springmvc/pom.xml
@@ -32,6 +32,10 @@
       <groupId>org.apache.servicecomb</groupId>
       <artifactId>swagger-invocation-core</artifactId>
     </dependency>
+    <dependency>
+      <groupId>com.google.inject</groupId>
+      <artifactId>guice</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-webmvc</artifactId>
diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/SpringMultipartArrayConverter.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/SpringMultipartArrayConverter.java
new file mode 100644
index 000000000..cebbc293f
--- /dev/null
+++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/SpringMultipartArrayConverter.java
@@ -0,0 +1,56 @@
+/*
+ * 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.invocation.converter;
+
+import java.lang.reflect.Type;
+import java.util.List;
+
+import javax.servlet.http.Part;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.google.inject.util.Types;
+
+
+@Component
+public class SpringMultipartArrayConverter implements CustomizedConverter {
+
+  @Override
+  public Type getSrcType() {
+    return Types.newParameterizedType(List.class, Part.class);
+  }
+
+  @Override
+  public Type getTargetType() {
+    return MultipartFile[].class;
+  }
+
+  @Override
+  public Object convert(Object value) {
+    if (value == null) {
+      return null;
+    }
+    @SuppressWarnings("unchecked")
+    List<Part> partList = (List<Part>) value;
+    PartToMultipartFile[] partArray = new PartToMultipartFile[partList.size()];
+    for (int i = 0; i < partArray.length; i++) {
+      partArray[i] = new PartToMultipartFile(partList.get(i));
+    }
+    return partArray;
+  }
+}
diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/SpringMultipartListConverter.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/SpringMultipartListConverter.java
new file mode 100644
index 000000000..7ed7ba934
--- /dev/null
+++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/SpringMultipartListConverter.java
@@ -0,0 +1,56 @@
+/*
+ * 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.invocation.converter;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.Part;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.google.inject.util.Types;
+
+@Component
+public class SpringMultipartListConverter implements CustomizedConverter {
+
+  @Override
+  public Type getSrcType() {
+    return Types.newParameterizedType(List.class, Part.class);
+  }
+
+  @Override
+  public Type getTargetType() {
+    return Types.newParameterizedType(List.class, MultipartFile.class);
+  }
+
+  @Override
+  public Object convert(Object value) {
+    if (value == null) {
+      return null;
+    }
+    @SuppressWarnings("unchecked")
+    List<Part> partList = (List<Part>) value;
+    List<PartToMultipartFile> fileList = new ArrayList<>();
+    partList.forEach(part -> {
+      fileList.add(new PartToMultipartFile(part));
+    });
+    return fileList;
+  }
+}


With regards,
Apache Git Services