2015-10-22 13 views
48

Chúng tôi đang phát triển máy chủ với API REST, chấp nhận và phản hồi với JSON. Vấn đề là, nếu bạn cần tải lên hình ảnh từ máy khách đến máy chủ. Lưu ý rằng tôi đang nói về trường hợp sử dụng, nơi thực thể (người dùng) có thể có các tệp (carPhoto, licensePhoto) và cũng có các thuộc tính khác (tên, email ...), nhưng khi bạn tạo người dùng mới, bạn không gửi những hình ảnh này, chúng được thêm sau quá trình đăng ký.REST API - tập tin (tức là hình ảnh) chế biến - thực hành tốt nhất


Các giải pháp tôi biết, nhưng mỗi người trong số họ có một số sai sót

1. Sử dụng multipart/form-data thay vì JSON

tốt: POST và PUT yêu cầu như RESTful càng tốt, chúng có thể chứa các đầu vào văn bản cùng với tệp.

khuyết điểm: Nó không phải là JSON nữa, đó là dễ dàng hơn nhiều để kiểm tra, gỡ lỗi, vv so sánh với multipart/form-data

2. Cho phép cập nhật tập tin riêng biệt theo yêu cầu

POST để tạo người dùng mới không cho phép thêm hình ảnh (trong trường hợp sử dụng của chúng tôi như thế nào khi tôi bắt đầu), tải lên hình ảnh được thực hiện theo yêu cầu PUT dưới dạng multipart/form-data cho ví dụ/người dùng/4/carPhoto

tốt: Mọi thứ (ngoại trừ việc tải lên tệp) vẫn còn trong JSON, thật dễ dàng để kiểm tra và gỡ lỗi (bạn có thể ghi lại các yêu cầu JSON hoàn chỉnh mà không sợ độ dài của chúng)

cons: Nó không trực quan, bạn không thể POST hoặc PUT tất cả các biến của thực thể cùng một lúc và cũng địa chỉ này /users/4/carPhoto có thể được xem xét nhiều hơn như một bộ sưu tập (trường hợp sử dụng chuẩn cho REST API trông như thế này /users/4/shipments). Thông thường bạn không thể (và không muốn) GET/PUT mỗi biến của thực thể, ví dụ như người dùng/4/tên. Bạn có thể lấy tên bằng GET và thay đổi nó bằng PUT ở người dùng/4. Nếu có cái gì đó sau khi id, nó thường là bộ sưu tập khác, như người dùng/4/đánh giá

3. Sử dụng Base64

Gửi nó như JSON nhưng file mã hóa với Base64.

tốt: Tương tự như giải pháp đầu tiên, dịch vụ RESTful càng tốt.

khuyết điểm: Một lần nữa, kiểm tra và gỡ lỗi là tồi tệ hơn rất nhiều (cơ thể có thể có megabyte dữ liệu), có sự gia tăng về kích thước và cũng trong thời gian xử lý trong cả hai - client và server


Tôi thực sự muốn sử dụng giải pháp không. 2, nhưng nó có khuyết điểm của nó ... Bất cứ ai cũng có thể cho tôi một cái nhìn sâu sắc hơn về "những gì là tốt nhất" giải pháp?

Mục tiêu của tôi là có các dịch vụ RESTful với nhiều tiêu chuẩn được bao gồm nhất có thể, trong khi tôi muốn giữ nó càng đơn giản càng tốt.

+0

Bạn cũng có thể thấy điều này hữu ích: http://stackoverflow.com/questions/4083702/posting-a-file-and-data-to-restful-webservice-as-json – Markon

+1

Tôi biết chủ đề này cũ nhưng chúng tôi đã gặp vấn đề này gần đây. Cách tiếp cận tốt nhất mà chúng tôi có được là tương tự như số của bạn 2. Chúng tôi tải tệp lên thẳng API và sau đó đính kèm các tệp này vào mô hình. Với kịch bản này, bạn có thể tạo hình ảnh tải lên trước, sau hoặc tại cùng một trang với biểu mẫu, không thực sự quan trọng. Thảo luận tốt! –

+1

@TiagoMatos - vâng, chính xác, tôi đã mô tả nó trong một câu trả lời mà gần đây tôi đã chấp nhận – libik

Trả lời

32

OP đây (tôi trả lời câu hỏi này sau hai năm, các bài do Daniel Cerecedo was not bad tại một thời điểm, nhưng các dịch vụ web đang phát triển rất nhanh)

Sau ba năm phát triển phần mềm toàn thời gian (tập trung vào kiến ​​trúc phần mềm, quản lý dự án và kiến ​​trúc microservice) Tôi chắc chắn chọn cách thứ hai (nhưng với một điểm cuối chung) là điểm tốt nhất.

Nếu bạn có điểm cuối đặc biệt cho hình ảnh, nó cung cấp cho bạn nhiều quyền lực hơn để xử lý hình ảnh đó.

Chúng tôi có cùng một API REST (Node.js) cho cả hai - ứng dụng dành cho thiết bị di động (iOS/android) và giao diện người dùng (sử dụng React). Đây là năm 2017, do đó bạn không muốn lưu trữ hình ảnh địa phương, bạn muốn tải chúng lên bộ nhớ đám mây (google cloud, s3, cloudinary, ...), do đó bạn muốn xử lý chung chúng.

Luồng điển hình của chúng tôi là ngay khi bạn chọn hình ảnh, nó sẽ bắt đầu tải lên trên nền (thường là điểm cuối POST/hình ảnh), trả lại ID cho bạn sau khi tải lên. Điều này thực sự thân thiện với người dùng, bởi vì người dùng chọn hình ảnh và sau đó thường tiến hành với một số trường khác (ví dụ: địa chỉ, tên, ...), do đó khi anh nhấn nút "gửi", hình ảnh thường được tải lên. Anh ta không đợi và xem màn hình nói "uploadiing ...".

Cũng vậy với việc chụp ảnh. Đặc biệt là nhờ điện thoại di động và dữ liệu di động hạn chế, bạn không muốn gửi hình ảnh gốc, bạn muốn gửi hình ảnh đã được chỉnh kích thước, vì vậy họ không lấy nhiều dữ liệu (và làm cho ứng dụng di động của bạn nhanh hơn, bạn thường không muốn thay đổi kích thước , bạn muốn hình ảnh phù hợp hoàn hảo với chế độ xem của bạn). Vì lý do này, các ứng dụng tốt đang sử dụng một cái gì đó như đám mây (hoặc chúng tôi có máy chủ hình ảnh của riêng chúng tôi để thay đổi kích thước).

Ngoài ra, nếu dữ liệu không phải là riêng tư, bạn sẽ gửi lại ứng dụng/giao diện chỉ URL và tải xuống từ bộ nhớ đám mây trực tiếp, tiết kiệm rất nhiều băng thông và thời gian xử lý cho máy chủ của bạn. Trong các ứng dụng lớn hơn của chúng tôi, có rất nhiều terabyte được tải xuống mỗi tháng, bạn không muốn xử lý trực tiếp trên mỗi máy chủ REST API của bạn, tập trung vào hoạt động CRUD. Bạn muốn xử lý tại một nơi (Máy chủ hình ảnh của chúng tôi, có bộ nhớ đệm, v.v.) hoặc cho phép các dịch vụ đám mây xử lý tất cả.


Nhược điểm: Chỉ "khuyết điểm" mà bạn nên nghĩ là "không được chỉ định hình ảnh".Người dùng chọn hình ảnh và tiếp tục điền vào các trường khác, nhưng sau đó anh ta nói "nah" và tắt ứng dụng hoặc tab, nhưng trong khi đó bạn đã tải lên hình ảnh thành công. Điều này có nghĩa là bạn đã tải lên hình ảnh không được chỉ định ở bất kỳ đâu.

Có một số cách để xử lý việc này. Cách dễ nhất là "Tôi không quan tâm", có liên quan, nếu điều này không xảy ra thường xuyên hoặc thậm chí bạn có mong muốn lưu trữ mọi người dùng gửi cho bạn (vì bất kỳ lý do nào) và bạn không muốn xóa.

Một cách khác cũng dễ dàng - bạn có CRON và nghĩa là mỗi tuần và bạn xóa tất cả hình ảnh chưa được chỉ định cũ hơn một tuần.

+0

Điều gì sẽ xảy ra nếu [ngay khi bạn chọn hình ảnh, nó bắt đầu tải lên trên nền (thường là POST/hình ảnh điểm cuối), trả lại ID sau khi tải lên] khi yêu cầu không thành công kết nối Internet? Bạn sẽ nhắc người dùng trong khi họ tiếp tục với một số trường khác (ví dụ: địa chỉ, tên, ...)? Tôi đặt cược bạn sẽ vẫn đợi cho đến khi người dùng nhấn nút "gửi" và thử lại yêu cầu của bạn khiến họ đợi trong khi xem màn hình có nội dung "tải lên ...". –

+1

@AdromilBalais - RESTful API là không trạng thái, do đó không có gì (Máy chủ không theo dõi trạng thái của người tiêu dùng). Người tiêu dùng dịch vụ (tức là trang web hoặc thiết bị di động) chịu trách nhiệm xử lý các yêu cầu không thành công, do đó người tiêu dùng phải quyết định xem có yêu cầu ngay lập tức sau khi yêu cầu này không thành công hoặc phải làm gì (tức là hiển thị "Tải lên hình ảnh không thành công - muốn thử lại ") – libik

+0

Câu trả lời rất thông tin và khai sáng. Cảm ơn bạn đã trả lời. –

4

Giải pháp thứ hai của bạn có lẽ là chính xác nhất. Bạn nên sử dụng thông số HTTP và kiểu mẫu theo cách chúng đã định và tải tệp lên qua multipart/form-data. Theo như xử lý các mối quan hệ, tôi muốn sử dụng quá trình này (giữ trong tâm trí tôi biết zero về các giả định của bạn hoặc thiết kế hệ thống):

  1. POST để /users để tạo ra các đối tượng người dùng.
  2. POST hình ảnh đến /images, đảm bảo trả lại tiêu đề Location cho nơi hình ảnh có thể được truy lục theo thông số HTTP.
  3. PATCH-/users/carPhoto và gán nó ID của ảnh được đưa ra trong Location tiêu đề của bước 2.
+1

Tôi không có quyền kiểm soát trực tiếp "khách hàng sẽ sử dụng API" ... Vấn đề ở đây là hình ảnh "chết" không được vá vào một số tài nguyên ... – libik

+3

Thông thường khi bạn chọn tùy chọn thứ hai, được ưu tiên để tải lên phần tử phương tiện đầu tiên và trả lại số nhận dạng phương tiện cho khách hàng, khi đó ứng dụng khách có thể gửi dữ liệu thực thể bao gồm số nhận dạng phương tiện, cách tiếp cận này tránh các thực thể bị hỏng thông tin không khớp. –

3

Không có giải pháp dễ dàng. Mỗi cách có ưu và khuyết điểm của họ. Nhưng cách kinh điển là sử dụng tùy chọn đầu tiên: multipart/form-data. Dưới dạng W3 recommendation guide nói

Loại nội dung "multipart/form-data" nên được sử dụng để gửi biểu mẫu chứa tệp, dữ liệu không phải ASCII và dữ liệu nhị phân.

Chúng tôi chưa gửi biểu mẫu, nhưng nguyên tắc ngầm vẫn được áp dụng. Sử dụng base64 làm biểu diễn nhị phân, không chính xác vì bạn đang sử dụng công cụ không chính xác để hoàn thành mục tiêu của mình, tùy chọn thứ hai buộc các ứng dụng API của bạn thực hiện nhiều công việc hơn để tiêu thụ dịch vụ API của bạn. Bạn nên làm công việc khó khăn ở phía máy chủ để cung cấp một API dễ tiêu thụ. Tùy chọn đầu tiên là không dễ dàng để gỡ lỗi, nhưng khi bạn làm điều đó, nó có thể không bao giờ thay đổi.

Sử dụng multipart/form-data bạn gắn bó với triết lý REST/http. Bạn có thể xem câu trả lời cho câu hỏi tương tự here.

Một tùy chọn khác nếu trộn các lựa chọn thay thế, bạn có thể sử dụng multipart/form-data nhưng thay vì gửi từng giá trị riêng biệt, bạn có thể gửi một giá trị có tên payload với trọng tải json bên trong nó. (Tôi đã thử phương pháp này bằng ASP.NET WebAPI 2 và hoạt động tốt).

+1

Hướng dẫn đề xuất W3 không liên quan ở đây vì nó nằm trong ngữ cảnh của thông số HTML 4. – Johann

+0

Rất đúng .... "non ASCII-data" yêu cầu multipart? Trong thế kỷ 21? Trong một thế giới UTF-8? Tất nhiên đó là một khuyến nghị vô lý cho ngày hôm nay. Tôi thậm chí còn ngạc nhiên khi tồn tại trong HTML 4 ngày, nhưng đôi khi thế giới cơ sở hạ tầng Internet di chuyển rất chậm. –

47

Có một số quyết định để làm cho:

  1. Đầu tiên về đường nguồn:

    • mẫu hình ảnh như một nguồn tài nguyên ngày của riêng mình:

      • Lồng nhau trong sử dụng (/ user /: id/hình ảnh): mối quan hệ giữa người sử dụng và các hình ảnh được thực hiện ngầm

      • Trong đường dẫn gốc (/ hình ảnh):

        • Các khách hàng được tổ chức chịu trách nhiệm về thiết lập mối quan hệ giữa hình ảnh và người dùng, hoặc;

        • Nếu bối cảnh bảo mật được cung cấp với yêu cầu POST được sử dụng để tạo hình ảnh, máy chủ hoàn toàn có thể thiết lập mối quan hệ giữa người dùng được xác thực và hình ảnh.

    • Nhúng hình ảnh như là một phần của người sử dụng

  2. Quyết định thứ hai là về làm thế nào để đại diện cho nguồn tài nguyên hình ảnh:

    • Là cơ sở 64 tải trọng JSON được mã hóa
    • Là một trọng tải nhiều phần dữ liệu

Đây sẽ là ca khúc quyết định của tôi:

  • Tôi thường ủng hộ thiết kế trên hiệu suất trừ khi có một trường hợp mạnh mẽ đối với nó. Nó làm cho hệ thống dễ bảo trì hơn và có thể dễ dàng được hiểu bởi các nhà tích hợp.
  • Vì vậy, suy nghĩ đầu tiên của tôi là đi cho một đại diện Base64 của nguồn hình ảnh bởi vì nó cho phép bạn giữ tất cả mọi thứ JSON. Nếu bạn chọn tùy chọn này, bạn có thể lập mô hình đường dẫn tài nguyên theo ý muốn.
    • Nếu mối quan hệ giữa người dùng và hình ảnh là 1 đến 1, tôi muốn mô hình hóa hình ảnh làm thuộc tính đặc biệt nếu cả hai tập dữ liệu được cập nhật cùng một lúc. Trong bất kỳ trường hợp nào khác, bạn có thể tự do chọn mô hình hình ảnh dưới dạng thuộc tính, cập nhật nó qua PUT hoặc PATCH hoặc dưới dạng tài nguyên riêng biệt.
  • Nếu bạn chọn tải trọng nhiều phần, tôi cảm thấy bắt buộc phải lập mô hình hình ảnh làm tài nguyên, để tài nguyên khác, trong trường hợp của chúng tôi, tài nguyên người dùng, không bị ảnh hưởng bởi quyết định sử dụng nhị phân đại diện cho hình ảnh.

Sau đó đến câu hỏi: Có bất kỳ tác động nào về hiệu suất về việc chọn base64 so với nhiều phần không?. Chúng tôi có thể nghĩ rằng trao đổi dữ liệu ở định dạng nhiều phần sẽ hiệu quả hơn. Nhưng this article cho thấy mức độ đại diện của chúng khác nhau bao nhiêu về kích thước.

lựa chọn Base64 của tôi:

  • quyết định thiết kế Phù
  • tác động hiệu quả không đáng kể
  • Do trình duyệt hiểu URI dữ liệu (hình ảnh mã hóa base64), không có cần phải chuyển đổi những nếu khách hàng là một trình duyệt
  • Tôi sẽ không bỏ phiếu cho dù có thuộc tính hoặc tài nguyên độc lập hay không, nó phụ thuộc vào miền sự cố của bạn (mà tôi không biết) và sở thích cá nhân của bạn.
+1

Chúng ta có thể mã hóa dữ liệu bằng cách sử dụng các giao thức tuần tự hóa khác như protobuf etc? Về cơ bản tôi đang cố gắng để hiểu nếu có những cách đơn giản khác để giải quyết kích thước và thời gian xử lý tăng mà đi kèm với mã hóa base64. –

+0

Câu trả lời rất hấp dẫn. cảm ơn vì cách tiếp cận từng bước. Nó làm cho tôi hiểu điểm của bạn tốt hơn rất nhiều. –

+0

Quan điểm rõ ràng, nhờ Daniel Cerecedo –

Các vấn đề liên quan