REST API-파일 (예 : 이미지) 처리-우수 사례


198

JSON으로 수락하고 응답하는 REST API를 사용하여 서버를 개발 중입니다. 문제는 클라이언트에서 서버로 이미지를 업로드해야하는 경우입니다.

참고 : 또한 엔티티 (사용자)가 여러 파일 (carPhoto, licensePhoto)을 가질 수 있고 다른 속성 (이름, 이메일 ...)을 가질 수있는 유스 케이스에 대해 이야기하고 있지만 새 사용자를 만들 때 이러한 이미지를 보내지 않으면 등록 프로세스 후에 추가됩니다.


내가 알고있는 솔루션이지만 각각의 결함이 있습니다.

1. JSON 대신 multipart / form-data를 사용하십시오.

good : POST 및 PUT 요청은 가능한 한 RESTful이며 텍스트 입력을 파일과 함께 포함 할 수 있습니다.

단점 : 더 이상 JSON이 아니므로 multipart / form-data와 비교하여 테스트, 디버그 등이 훨씬 쉽습니다.

2. 별도의 파일 업데이트 허용

새 사용자를 만들기위한 POST 요청은 이미지를 추가 할 수 없으며 (이것은 유스 케이스에서 처음에 말한대로 괜찮습니다) PUT 요청에 의해 그림 업로드는 / parts / 4 / carPhoto와 같은 multipart / form-data로 수행됩니다

good : 파일 업로드 자체를 제외한 모든 것이 JSON에 남아 있으며 테스트 및 디버깅이 쉽습니다 (길이를 두려워하지 않고 완전한 JSON 요청을 기록 할 수 있음)

단점 : 직관적이지 않고 엔티티의 모든 변수를 한 번에 POST 또는 PUT 할 수 없으며이 주소 /users/4/carPhoto는 컬렉션으로 간주 될 수 있습니다 (REST API의 표준 사용 사례는 다음과 같습니다 /users/4/shipments). 일반적으로 엔티티의 각 변수를 GET / PUT 할 수 없습니다 (예 : users / 4 / name). GET으로 이름을 얻고 사용자 / 4에서 PUT으로 변경할 수 있습니다. ID 뒤에 무언가가 있으면 일반적으로 users / 4 / reviews와 같은 다른 컬렉션입니다.

3. Base64 사용

JSON으로 보내지 만 Base64로 파일을 인코딩하십시오.

good : 첫 번째 솔루션과 동일하며 가능한 한 RESTful 서비스입니다.

단점 : 다시 한번, 테스트 및 디버깅이 훨씬 나 빠지고 (본문은 메가 바이트의 데이터를 가질 수 있음) 클라이언트 및 서버 모두에서 크기가 증가하고 처리 시간이 증가합니다.


솔루션 번호를 정말로 사용하고 싶습니다. 2, 그러나 그것의 단점이 있습니다. 누구든지 "최고의 솔루션"에 대한 더 나은 통찰력을 줄 수 있습니까?

저의 목표는 가능한 한 많은 표준이 포함 된 RESTful 서비스를 제공하는 동시에 가능한 한 단순하게 유지하고 싶습니다.



5
이 주제는 오래되었지만 최근이 문제에 직면했습니다. 우리가 얻는 가장 좋은 방법은 2 번과 비슷합니다. 파일을 API에 바로 업로드 한 다음이 파일을 모델에 첨부하십시오. 이 시나리오를 사용하면 양식 이전, 이후 또는 동일한 페이지에서 업로드 이미지를 만들 수 있습니다. 좋은 토론!
Tiago Matos

2
@TiagoMatos-그렇습니다. 정확히 내가 최근에 수락 한 한 가지 답변으로 설명했습니다
libik

6
이 질문을 해주셔서 감사합니다.
Zuhayer Tahir

1
"또한이 주소 / users / 4 / carPhoto는 모음으로 간주 될 수 있습니다"– 모음처럼 보이지 않으며 반드시 하나로 간주되지는 않습니다. 컬렉션이 아니라 단일 리소스 인 리소스와 관계를 갖는 것이 좋습니다.
B12Toaster

답변:


156

OP 여기 (2 년 후에이 질문에 대답하고 있습니다 .Daniel Cerecedo의 게시물은 한 번에 나쁘지 않았지만 웹 서비스는 매우 빠르게 발전하고 있습니다)

3 년의 풀 타임 소프트웨어 개발 (소프트웨어 아키텍처, 프로젝트 관리 및 마이크로 서비스 아키텍처에 중점을 둔) 이후, 저는 두 번째 방법 (일반적인 엔드 포인트가있는)을 최고의 것으로 선택했습니다.

이미지에 대한 특별한 엔드 포인트가있는 경우 해당 이미지를 처리하는 데 훨씬 더 많은 힘을줍니다.

모바일 앱 (iOS / android)과 프론트 엔드 (React 사용) 모두에 대해 동일한 REST API (Node.js)가 있습니다. 이것은 2017 년이므로 이미지를 로컬로 저장하지 않고 일부 클라우드 저장소 (Google 클라우드, s3, cloudinary 등)에 업로드하려고하므로 일반적인 처리가 필요합니다.

일반적인 흐름은 이미지를 선택하자마자 백그라운드에서 업로드를 시작하고 (일반적으로 / images 엔드 포인트에서 POST) 업로드 후 ID를 반환합니다. 사용자가 이미지를 선택한 다음 일반적으로 다른 필드 (예 : 주소, 이름 등)를 진행하므로 "보내기"버튼을 누르면 이미지가 이미 업로드되기 때문에 이는 실제로 사용자에게 친숙합니다. 그는 "업로드 중 ..."이라는 화면을 기다리지 않고 기다립니다.

이미지를 얻는 것도 마찬가지입니다. 특히 휴대 전화와 제한된 모바일 데이터 덕분에 원본 이미지를 보내지 않고 크기가 조정 된 이미지를 보내려고하므로 대역폭을 많이 차지하지 않으며 모바일 앱의 속도를 높이기 위해 원하지 않는 경우가 많습니다 크기를 전혀 조정하지 않으려면 뷰에 완벽하게 맞는 이미지가 필요합니다. 이러한 이유로 좋은 앱은 cloudinary와 같은 것을 사용하고 있습니다 (또는 크기 조정을 위해 자체 이미지 서버가 있습니다).

또한 데이터가 개인 정보가 아닌 경우 URL을 앱 / 프런트 엔드로 다시 전송하여 클라우드 저장소에서 직접 다운로드하므로 서버의 대역폭 및 처리 시간이 크게 절약됩니다. 더 큰 앱에는 매달 다운로드되는 테라 바이트가 많으므로 CRUD 작업에 중점을 둔 각 REST API 서버에서 직접 처리하고 싶지 않습니다. 한 곳에서 (캐싱이있는 Imageserver) 처리하거나 클라우드 서비스가 모든 것을 처리하도록하십시오.


단점 : 당신이 생각해야 할 유일한 "단점"은 "할당되지 않은 이미지"입니다. 사용자는 이미지를 선택하고 다른 필드를 계속 채우고 "nah"라고 말하고 앱 또는 탭을 끄지 만 이미지를 성공적으로 업로드했습니다. 이것은 어느 곳에도 할당되지 않은 이미지를 업로드했음을 의미합니다.

이를 처리하는 몇 가지 방법이 있습니다. 가장 쉬운 방법은 "I do n't care"입니다. 이것은 매우 자주 발생하지 않거나 사용자가 보내는 모든 이미지를 저장하고 싶을 때 (어떤 이유로 든) 원하지 않는 경우 관련된 것입니다. 삭제.

또 하나는 쉽습니다-매주 CRON을 가지고 있고 일주일이 지난 할당되지 않은 모든 이미지를 삭제합니다.


인터넷 연결로 인해 요청이 실패했을 때 [이미지를 선택하자마자 이미지를 업로드하자마자 백그라운드에서 업로드를 시작하고 (일반적으로 POST on / images 엔드 포인트) 업로드 후 ID를 반환 함) 어떻게됩니까? 다른 필드 (예 : 주소, 이름 등)를 진행하는 동안 사용자에게 메시지가 표시됩니까? 사용자가 "보내기"버튼을 누를 때까지 기다렸다가 "업로드 중 ..."이라는 화면을 보면서 기다리는 동안 요청을 다시 시도하십시오.
Adromil Balais

5
@AdromilBalais-RESTful API는 상태 비 저장이므로 아무것도 수행하지 않습니다 (서버가 소비자 상태를 추적하지 않음). 서비스 소비자 (예 : 웹 페이지 또는 모바일 장치)는 실패한 요청을 처리 할 책임이 있으므로 소비자는이 요청이 실패한 후 바로 동일한 요청을 호출 할 것인지 또는 수행 할 작업을 결정해야합니다 (예 : "이미지 업로드 실패-다시 시도하려고 함) ")
libik

2
매우 유익하고 계몽적인 답변. 대답 해줘서 고마워.
Zuhayer Tahir

이렇게해도 초기 문제가 해결되지는 않습니다. 이것은 단지 "클라우드 서비스 사용"이라고 말합니다
Martin Muzatko

3
@MartinMuzatko-그것은 두 번째 옵션을 선택하고 사용 방법과 이유를 알려줍니다. "그러나 이것이 하나의 요청으로 모든 것을 보낼 수있는 완벽한 옵션은 아닙니다"-예, 불행히도 그러한 해결책은 없습니다.
libik

105

몇 가지 결정을 내릴 수 있습니다 .

  1. 리소스 경로 에 대한 첫 번째 :

    • 이미지를 자체 리소스로 모델링하십시오.

      • 사용자에 중첩 (/ user / : id / image) : 사용자와 이미지 간의 관계가 암시 적으로 만들어 짐

      • 루트 경로 (/ 이미지)에서 :

        • 고객은 이미지와 사용자 간의 관계를 설정해야 할 책임이 있습니다.

        • 이미지를 작성하는 데 사용 된 POST 요청과 함께 보안 컨텍스트가 제공되는 경우 서버는 인증 된 사용자와 이미지 사이의 관계를 내재적으로 설정할 수 있습니다.

    • 사용자의 일부로 이미지를 포함

  2. 두 번째 결정은 이미지 리소스를 나타내는 방법에 관한 것입니다 .

    • Base 64로 인코딩 된 JSON 페이로드
    • 멀티 파트 페이로드

이것은 내 결정 트랙이 될 것입니다 :

  • 강한 경우가 없다면 성능보다는 디자인을 선호합니다. 이는 시스템을보다 유지 보수 할 수있게하고 통합자가보다 쉽게 ​​이해할 수있게합니다.
  • 그래서 첫 번째 생각은 모든 JSON을 유지할 수 있기 때문에 이미지 리소스의 Base64 표현을 사용하는 것입니다. 이 옵션을 선택한 경우 원하는대로 리소스 경로를 모델링 할 수 있습니다.
    • 사용자와 이미지의 관계가 1 대 1 인 경우 두 데이터 세트가 동시에 업데이트되는 경우 이미지를 속성으로 모델링하는 것이 좋습니다. 다른 경우에는 이미지를 속성, PUT 또는 PATCH를 통해 업데이트하거나 별도의 리소스로 자유롭게 모델링 할 수 있습니다.
  • multipart payload를 선택하면 이미지를 리소스 자체로 모델링해야한다고 생각합니다.이 경우 사용자 리소스는 다른 리소스가 이미지에 이진 표현을 사용하는 결정의 영향을받지 않습니다.

그러면 base64와 multipart를 선택하는 데 성능에 영향이 있습니까? . 멀티 파트 형식으로 데이터를 교환하는 것이 더 효율적이라고 생각할 수 있습니다. 그러나이 기사 에서는 두 표현의 크기가 얼마나 다른지 보여줍니다.

내가 선택한 Base64 :

  • 일관된 설계 결정
  • 무시할만한 성능 영향
  • 브라우저는 데이터 URI (base64 인코딩 이미지)를 이해하므로 클라이언트가 브라우저 인 경우이를 변환 할 필요가 없습니다.
  • 나는 속성 또는 독립형 리소스로 투표 할 것인지에 대한 투표를하지 않을 것입니다. 문제 도메인 (알지 못하는)과 개인적 선호도에 따라 다릅니다.

3
protobuf 등과 같은 다른 직렬화 프로토콜을 사용하여 데이터를 인코딩 할 수 없습니까? 기본적으로 base64 인코딩과 함께 제공되는 크기 및 처리 시간 증가를 해결하는 다른 간단한 방법이 있는지 이해하려고합니다.
Andy Dufresne 8

1
매우 매력적인 답변. 단계별 접근 방식에 감사드립니다. 그것은 당신의 요점을 훨씬 더 잘 이해하게 만들었습니다.
Zuhayer Tahir

13

두 번째 해결책이 가장 정확할 것입니다. HTTP 사양과 mimetype을 의도 한대로 사용하고를 통해 파일을 업로드해야합니다 multipart/form-data. 관계를 처리하는 한이 프로세스를 사용합니다 (귀하의 가정 또는 시스템 설계에 대해 전혀 알고 있음을 명심하십시오).

  1. POST하는 /users사용자 개체를 만들 수 있습니다.
  2. POST이미지는합니다 /images, 확인하는 것은 반환 할 Location이미지가 HTTP 사양에 따라 검색 할 수있는 위치에 헤더를.
  3. PATCH/users/carPhoto주어진 사진의 ID를 할당하고 Location2 단계의 헤더.

1
"클라이언트가 API를 사용하는 방법"을 직접 제어 할 수는 없습니다.이 문제는 일부 리소스에 패치되지 않은 "죽은"그림입니다.
libik

4
일반적으로 두 번째 옵션을 선택할 때 먼저 미디어 요소를 업로드하고 클라이언트로 미디어 식별자를 반환하는 것이 좋습니다. 클라이언트는 미디어 식별자를 포함하여 엔터티 데이터를 보낼 수 있습니다. 이러한 접근 방식은 엔티티가 일치하지 않는 것을 방지합니다.
Kellerman Rivero

2

쉬운 해결책은 없습니다. 각 방법에는 장단점이 있습니다. 그러나 정식 방법은 첫 번째 옵션을 사용하는 것입니다 multipart/form-data. 로 W3 추천 가이드는 말한다

컨텐츠 유형 "multipart / form-data"는 파일, 비 ASCII 데이터 및 2 진 데이터를 포함하는 양식을 제출하는 데 사용해야합니다.

실제로 양식을 보내지는 않지만 암묵적인 원칙은 여전히 ​​적용됩니다. base64를 이진 표현으로 사용하면 목표를 달성하기 위해 잘못된 도구를 사용하기 때문에 올바르지 않습니다. 반면에 두 번째 옵션은 API 서비스를 소비하기 위해 API 클라이언트가 더 많은 작업을 수행하도록합니다. 사용하기 쉬운 API를 제공하려면 서버 측에서 열심히해야합니다. 첫 번째 옵션은 디버깅하기 쉽지 않지만 그렇게하면 변경되지 않을 것입니다.

multipart/form-data당신을 사용 하는 것은 REST / http 철학에 충실합니다. 비슷한 질문에 대한 답변을 여기에서 볼 수 있습니다 .

대안을 혼합하는 경우 다른 옵션은 multipart / form-data를 사용할 수 있지만 모든 값을 별도로 보내지 않고 내부에 json 페이로드와 함께 payload라는 값을 보낼 수 있습니다. (ASP.NET WebAPI 2를 사용 하여이 방법을 시도했지만 정상적으로 작동합니다).


2
이 W3 권장 가이드는 HTML 4 사양과 관련되어 있으므로 여기서는 관련이 없습니다.
Johann

1
매우 사실 .... "비 ASCII 데이터"에는 멀티 파트가 필요합니까? 21 세기에? UTF-8 세계에서? 물론 그것은 오늘 어리석은 추천입니다. HTML 4 일에 존재한다는 사실에 놀랐지 만 때로는 인터넷 인프라 세계가 매우 느리게 움직입니다.
Ray Toal
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.