Yêu cầu đa phần Spring MVC với JSON


83

Tôi muốn đăng một tệp có một số dữ liệu JSON bằng Spring MVC. Vì vậy, tôi đã phát triển một dịch vụ nghỉ ngơi như

@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);
}

Khi tôi gửi yêu cầu từ khách hàng còn lại content-Type = multipart/form-data or multipart/mixed, tôi nhận được ngoại lệ tiếp theo: org.springframework.web.multipart.support.MissingServletRequestPartException

Bất cứ ai có thể giúp tôi giải quyết vấn đề này?

Tôi có thể sử dụng @RequestPartđể gửi cả Multipart và JSON đến một máy chủ không?


Bạn đã chỉ định một org.springframework.web.multipart.commons.CommonsMultipartResolvertrong ngữ cảnh servlet của mình chưa?
Will Keeling

vâng, nó được thêm vào spring.xml của tôi. <bean id = "multipleartResolver" class = "org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name = "maxUploadSize" value = "300000000" /> </bean>
Sunil Kumar

Câu trả lời:


198

Đây là cách tôi triển khai Yêu cầu đa phần Spring MVC với Dữ liệu JSON.

Yêu cầu đa phần với dữ liệu JSON (còn được gọi là Đa phần hỗn hợp):

Dựa trên dịch vụ RESTful trong Bản phát hành Spring 4.0.2, yêu cầu HTTP với phần đầu tiên là dữ liệu được định dạng XML hoặc JSON và phần thứ hai dưới dạng tệp có thể đạt được với @RequestPart. Dưới đây là thực hiện mẫu.

Đoạn mã Java:

Dịch vụ khôi phục trong Bộ điều khiển sẽ có @RequestPart và MultipartFile hỗn hợp để phục vụ yêu cầu 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);
}

Đoạn mã giao diện người dùng (JavaScript):

  1. Tạo một đối tượng FormData.

  2. Nối tệp vào đối tượng FormData bằng một trong các bước dưới đây.

    1. Nếu tệp đã được tải lên bằng phần tử đầu vào thuộc loại "tệp", thì hãy nối tệp đó vào đối tượng FormData. formData.append("file", document.forms[formName].file.files[0]);
    2. Nối trực tiếp tệp vào đối tượng FormData. formData.append("file", myFile, "myfile.txt");HOẶC LÀformData.append("file", myBob, "myfile.txt");
  3. Tạo một đốm màu với dữ liệu JSON được xâu chuỗi và nối nó vào đối tượng FormData. Điều này khiến Kiểu nội dung của phần thứ hai trong yêu cầu nhiều phần là "ứng dụng / json" thay vì loại tệp.

  4. Gửi yêu cầu đến máy chủ.

  5. Yêu cầu chi tiết:
    Content-Type: undefined. Điều này khiến trình duyệt đặt Loại-Nội dung thành nhiều phần / biểu mẫu-dữ liệu và điền chính xác ranh giới. Việc đặt Content-Type thành nhiều phần / biểu mẫu-dữ liệu theo cách thủ công sẽ không điền được thông số ranh giới của yêu cầu.

Mã 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"
            }));

Yêu cầu chi tiết:

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

Yêu cầu tải trọng:

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
Tốt lắm. Tôi đã phải sử dụng processData: false, contentType: falsevớiJQuery $ajax()
sura2k

1
@SunilKumar, Nếu tôi cần cho phép tải tệp lên là tùy chọn ..? Với Dữ liệu biểu mẫu. Làm cách nào để thực hiện việc này. Bởi vì nếu hình ảnh không được chọn, tôi sẽ nhận đượcRequired request part file is not present
Hema

1
Đối với tôi, phần "Blob mới ([JSON.stringify (...)]" đã làm được điều đó .. Tôi đã có mọi thứ khác ở vị trí cũ. Thx.
Ostati

4
@SunilKumar bạn có phải chỉ định Converter cho ConnectionProperties không? Nếu tôi sử dụng một pojo như được hiển thị ở trên cho ConnectionProperties, tôi nhận được ... HttpMediaTypeNotSupportedException: Loại nội dung 'ứng dụng / octet-stream' không được hỗ trợ Nếu tôi thay đổi POJO thành Chuỗi thì nó hoạt động. Vì vậy, không rõ việc chuyển đổi sang POJO đang diễn ra như thế nào?
user2412398

1
Chỉ cần làm rõ điều này: @NotBlankChú thích trên tham số phương thức MultipartFile sẽ không thực sự kiểm tra xem tệp có trống hay không. Vẫn có thể tải lên các tài liệu có 0 byte.
sn42,

14

Điều này phải hoạt động!

khách hàng (góc cạnh):

$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;
        }
      };

Khởi động Backend-Spring:

@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

Như tài liệu cho biết:

Tăng lên khi không thể tìm thấy một phần của yêu cầu "nhiều phần / biểu mẫu-dữ liệu" được xác định bằng tên của nó.

Điều này có thể là do yêu cầu không phải là dữ liệu đa phần / biểu mẫu do phần này không có trong yêu cầu hoặc do ứng dụng web không được định cấu hình chính xác để xử lý các yêu cầu nhiều phần - ví dụ như không có MultipartResolver.


0

Chúng tôi đã thấy trong các dự án của mình rằng một yêu cầu đăng với JSON và các tệp đang tạo ra nhiều sự nhầm lẫn giữa các nhà phát triển giao diện người dùng và phụ trợ, dẫn đến sự lãng phí thời gian không cần thiết.

Đây là một cách tiếp cận tốt hơn: chuyển đổi mảng byte tệp thành chuỗi Base64 và gửi nó trong 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());
    ....
}

Mã JS để chuyển đổi tệp thành chuỗi 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);
};
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.