Весняний багаточастинний запит MVC із JSON


84

Я хочу опублікувати файл із деякими даними JSON за допомогою Spring MVC. Тож я розробив послугу відпочинку як

@RequestMapping(value = "/servicegenerator/wsdl", method = RequestMethod.POST,consumes = { "multipart/mixed", "multipart/form-data" })
@ResponseBody
public String generateWSDLService(@RequestPart("meta-data") WSDLInfo wsdlInfo,@RequestPart("file") MultipartFile file) throws WSDLException, IOException,
        JAXBException, ParserConfigurationException, SAXException, TransformerException {
    return handleWSDL(wsdlInfo,file);
}

Коли я відправляю запит від решти клієнта за допомогою content-Type = multipart/form-data or multipart/mixed, я отримую наступний виняток: org.springframework.web.multipart.support.MissingServletRequestPartException

Хто-небудь може мені допомогти у вирішенні цього питання?

Чи можу я використовувати @RequestPartдля надсилання як Multipart, так і JSON на сервер?


Ви вказали a org.springframework.web.multipart.commons.CommonsMultipartResolverу своєму контексті сервлету?
Уілл Кілінг

так, це додано в моєму spring.xml. <bean id = "multipartResolver" class = "org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name = "maxUploadSize" value = "300000000" /> </bean>
Суніл Кумар

Відповіді:


199

Ось як я реалізував Spring MVC Multipart Request із даними JSON.

Багаточастинний запит із даними JSON (також званий змішаним багаточастинним):

На основі служби RESTful у версії 4.0.2, HTTP-запит із першою частиною як дані у форматі XML або JSON, а з другою частиною як файл можна отримати за допомогою @RequestPart. Нижче наведено зразок реалізації.

Фрагмент Java:

Служба відпочинку в Controller буде мішати @RequestPart та MultipartFile для обслуговування такого запиту Multipart + JSON.

@RequestMapping(value = "/executesampleservice", method = RequestMethod.POST,
    consumes = {"multipart/form-data"})
@ResponseBody
public boolean executeSampleService(
        @RequestPart("properties") @Valid ConnectionProperties properties,
        @RequestPart("file") @Valid @NotNull @NotBlank MultipartFile file) {
    return projectService.executeSampleService(properties, file);
}

Фрагмент фрагмента (JavaScript):

  1. Створіть об'єкт FormData.

  2. Додайте файл до об’єкта FormData, виконавши один із наведених нижче кроків.

    1. Якщо файл було завантажено за допомогою елемента вводу типу "файл", додайте його до об'єкта FormData. formData.append("file", document.forms[formName].file.files[0]);
    2. Безпосередньо додайте файл до об’єкта FormData. formData.append("file", myFile, "myfile.txt");АБОformData.append("file", myBob, "myfile.txt");
  3. Створіть крапку із розширеними даними JSON і додайте її до об'єкта FormData. Це спричиняє тип вмісту другої частини у багаточастинному запиті "application / json" замість типу файлу.

  4. Надішліть запит на сервер.

  5. Запит про подробицях:
    Content-Type: undefined. Це змушує браузер встановити Content-Type на multipart / form-data і правильно заповнити межу. Вручну встановивши Content-Type на multipart / form-data, не вдасться заповнити граничний параметр запиту.

Код Javascript:

formData = new FormData();

formData.append("file", document.forms[formName].file.files[0]);
formData.append('properties', new Blob([JSON.stringify({
                "name": "root",
                "password": "root"                    
            })], {
                type: "application/json"
            }));

Деталі запиту:

method: "POST",
headers: {
         "Content-Type": undefined
  },
data: formData

Запит на корисне навантаження:

Accept:application/json, text/plain, */*
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryEBoJzS3HQ4PgE1QB

------WebKitFormBoundaryvijcWI2ZrZQ8xEBN
Content-Disposition: form-data; name="file"; filename="myfile.txt"
Content-Type: application/txt


------WebKitFormBoundaryvijcWI2ZrZQ8xEBN
Content-Disposition: form-data; name="properties"; filename="blob"
Content-Type: application/json


------WebKitFormBoundaryvijcWI2ZrZQ8xEBN--

1
Чудово зроблено. Мені довелося використовувати processData: false, contentType: falseзJQuery $ajax()
sura2k

1
@SunilKumar, Якщо мені потрібно надати завантаження файлів як необов’язкове ..? Із формою Data.How я можу це зробити. Тому що, якщо зображення не вибрано, я отримуюRequired request part file is not present
Хема

1
Для мене це зробила частина "новий Blob ([JSON.stringify (...)]"). У мене було все інше на місці. Thx.
Остаті

4
@SunilKumar Вам доводилося вказувати Converter для ConnectionProperties? Якщо я використовую pojo, як показано вище для ConnectionProperties, я отримую ... HttpMediaTypeNotSupportedException: Тип вмісту 'application / octet-stream' не підтримується. Якщо я змінив POJO на String, він працює. Тож незрозуміло, як відбувається перехід на POJO?
user2412398

1
Просто щоб це було зрозуміло: @NotBlankанотація параметра параметра MultipartFile насправді не перевіряє, чи файл порожній. Завантажити документи обсягом 0 байт все ще можна.
sn42

14

Це має спрацювати!

клієнт (кутовий):

$scope.saveForm = function () {
      var formData = new FormData();
      var file = $scope.myFile;
      var json = $scope.myJson;
      formData.append("file", file);
      formData.append("ad",JSON.stringify(json));//important: convert to JSON!
      var req = {
        url: '/upload',
        method: 'POST',
        headers: {'Content-Type': undefined},
        data: formData,
        transformRequest: function (data, headersGetterFunction) {
          return data;
        }
      };

Завантажувальне-весняне завантаження:

@RequestMapping(value = "/upload", method = RequestMethod.POST)
    public @ResponseBody
    Advertisement storeAd(@RequestPart("ad") String adString, @RequestPart("file") MultipartFile file) throws IOException {

        Advertisement jsonAd = new ObjectMapper().readValue(adString, Advertisement.class);
//do whatever you want with your file and jsonAd

1

Як сказано в документації:

Підвищується, коли частину запиту "multipart / form-data", визначену за його назвою, не вдається знайти.

Це може бути тому, що запит не є даними із декількох частин / форм, або тому, що частина відсутня у запиті, або тому, що веб-програма налаштована неправильно для обробки багаточастинних запитів - наприклад, не MultipartResolver.


0

У наших проектах ми бачили, що запит на публікацію з JSON та файлами створює велику плутанину між розробниками інтерфейсу та бекенда, що призводить до зайвої втрати часу.

Ось кращий підхід: перетворіть масив байтів файлів у рядок Base64 і надішліть його в JSON.

public Class UserDTO {
    private String firstName;
    private String lastName;
    private FileDTO profilePic; 
}

public class FileDTO {
    private String base64;
    // just base64 string is enough. If you want, send additional details
    private String name;
    private String type;
    private String lastModified;
}

@PostMapping("/user")
public String saveUser(@RequestBody UserDTO user) {
    byte[] fileBytes = Base64Utils.decodeFromString(user.getProfilePic().getBase64());
    ....
}

Код JS для перетворення файлу в рядок base64:

var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {

  const userDTO = {
    firstName: "John",
    lastName: "Wick",
    profilePic: {
      base64: reader.result,
      name: file.name,
      lastModified: file.lastModified,
      type: file.type
    }
  }
  
  // post userDTO
};
reader.onerror = function (error) {
  console.log('Error: ', error);
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.