JSON을 사용한 Spring MVC Multipart 요청


84

Spring MVC를 사용하여 JSON 데이터가있는 파일을 게시하고 싶습니다. 그래서 휴식 서비스를 개발했습니다.

@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

누구든지이 문제를 해결하는 데 도움을 줄 수 있습니까?

@RequestPartMultipart와 JSON을 모두 서버에 보낼 수 있습니까 ?


org.springframework.web.multipart.commons.CommonsMultipartResolver서블릿 컨텍스트에서 를 지정 했습니까 ?
Will Keeling 2014 년

예, 내 spring.xml에 추가되었습니다. <bean id = "multipartResolver"class = "org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name = "maxUploadSize"value = "300000000"/> </ bean>
Sunil Kumar

답변:


199

이것이 JSON 데이터로 Spring MVC Multipart Request를 구현 한 방법입니다.

JSON 데이터가 포함 된 멀티 파트 요청 (혼합 멀티 파트라고도 함) :

Spring 4.0.2 Release의 RESTful 서비스를 기반으로 첫 번째 부분은 XML 또는 JSON 형식의 데이터로, 두 번째 부분은 파일로 HTTP 요청은 @RequestPart로 수행 할 수 있습니다. 다음은 샘플 구현입니다.

자바 스 니펫 :

Controller의 Rest 서비스에는 이러한 Multipart + JSON 요청을 처리하기 위해 @RequestPart와 MultipartFile이 혼합되어 있습니다.

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

프런트 엔드 (자바 스크립트) 스 니펫 :

  1. FormData 개체를 만듭니다.

  2. 아래 단계 중 하나를 사용하여 FormData 개체에 파일을 추가합니다.

    1. 파일이 "file"유형의 입력 요소를 사용하여 업로드 된 경우 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 데이터로 Blob을 만들고 FormData 개체에 추가합니다. 이로 인해 다중 파트 요청에서 두 번째 파트의 컨텐츠 유형이 파일 유형 대신 "application / json"이됩니다.

  4. 서버에 요청을 보냅니다.

  5. 요청 세부 정보 :
    Content-Type: undefined. 이로 인해 브라우저는 Content-Type을 multipart / form-data로 설정하고 경계를 올바르게 채 웁니다. Content-Type을 multipart / form-data로 수동 설정하면 요청의 경계 매개 변수를 채우지 못합니다.

자바 스크립트 코드 :

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, 양식 데이터와 함께 파일 업로드를 선택 사항으로 제공해야하는 경우 어떻게해야합니까? 왜냐하면 이미지가 선택되지 않으면 받고 있습니다Required request part file is not present
Hema

1
나를 위해 "new Blob ([JSON.stringify (...)]"부분이 그것을했습니다 .. 다른 모든 것을 제자리에
두었

4
@SunilKumar ConnectionProperties에 대한 변환기를 지정해야 했습니까? ConnectionProperties에 대해 위와 같은 pojo를 사용하면 ... HttpMediaTypeNotSupportedException : 콘텐츠 유형 'application / octet-stream'이 지원되지 않습니다. POJO를 String으로 변경하면 작동합니다. 그래서 POJO 로의 변환이 어떻게 일어나는지 명확하지 않습니까?
user2412398

1
이를 명확히하기 위해 @NotBlankMultipartFile 메서드 매개 변수 의 주석은 파일이 비어 있는지 실제로 확인하지 않습니다. 여전히 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());
    ....
}

파일을 base64 문자열로 변환하는 JS 코드 :

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);
};
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.