web-dev-qa-db-ja.com

REST AP​​I - ファイル(画像)処理 - ベストプラクティス

JSONを受け入れて応答するREST AP​​Iを使用してサーバーを開発しています。問題は、クライアントからサーバーに画像をアップロードする必要がある場合です。

また、私はユースケースについて話していることに注意してください。エンティティ(ユーザー)はファイル(carPhoto、licensePhoto)を持ち、他のプロパティ(name、email ...)を持つことができます。登録プロセスの後に追加されます。


私が知っている解決策、しかしそれらのそれぞれにはいくつかの欠陥があります

1. JSONの代わりにmultipart/form-dataを使う

good:POSTおよびPUT要求は可能な限りRESTfulで、ファイルと一緒にテキスト入力を含めることができます。

cons:JSONではなくなりました。multipart/form-dataに比べてテストやデバッグなどがはるかに簡単です。

2。別々のファイルを更新することを許可する

新しいユーザーを作成するためのPOSTリクエストでは画像を追加することはできません(私たちのユースケースでは申し上げましたが大丈夫です)。PUTリクエストによってmultipart/form-dataとして/ users/4/carPhotoにアップロードされます。

good:(ファイルのアップロード自体を除く)すべてがJSONに残っているので、テストとデバッグが簡単です(完全なJSON要求を恐れずに記録できます)。彼らの長さ)

cons:それは直感的ではありません、POST、またはentityのすべての変数を一度に傾けることはできず、このアドレス/users/4/carPhotoも考慮することができますコレクションとしての機能(REST AP​​Iの標準的な使用例は、この/users/4/shipmentsのようになります)。通常、users/4/nameのように、エンティティの各変数をGET/PUTできません。 GETで名前を取得し、users/4のPUTでそれを変更することができます。 idの後に何かある場合は、通常users/4/reviewsのように別のコレクションです。

。Base64を使う

JSONとして送信しますが、Base64でファイルをエンコードします。

good:最初の解決策と同じですが、可能な限りRESTfulなサービスです。

短所:やはり、テストとデバッグはさらに悪くなり(本体には数メガバイトのデータがある可能性があります)、サイズが大きくなり、処理時間も長くなります両方 - クライアントとサーバー


私は本当に解決策番号を使用したいと思います。 2、しかしそれはその短所を持っています...誰が私に "何が最善である"という解決策のより良い洞察力を与えることができますか?

私の目標は、できる限り多くの標準を含めてRESTfulサービスを提供することですが、それをできるだけ単純にしたいのです。

108
libik

OP here(私は2年後にこの質問に答えています、Daniel Cerecedoによる投稿は一度に悪くなかったのですが、Webサービスは急速に発展しています)

年間のフルタイムソフトウェア開発の後(ソフトウェアアーキテクチャ、プロジェクト管理、マイクロサービスアーキテクチャにも焦点を当てて)私は間違いなく2番目の方法(ただし1つの一般的なエンドポイント)を最善の方法として選択します。

あなたが画像のための特別な終点を持っているならば、それはあなたにそれらの画像を扱うよりはるかに大きな力を与えます。

モバイルアプリ(iOS/Android)とフロントエンド(Reactを使用)の両方に対して同じREST AP​​I(Node.js)があります。これは2017年なので、画像をローカルに保存するのではなく、アップロードしてクラウドストレージ(Googleクラウド、s3、cloudinaryなど)にしたいので、一般的な処理をしたいのです。

私たちの典型的な流れは、あなたが画像を選択するとすぐにそれがバックグラウンドでアップロードを開始し(通常POST on/imagesエンドポイント)、アップロード後にあなたにIDを返すことです。ユーザーは画像を選択してから他のいくつかのフィールド(アドレス、名前など)に進むので、これは本当にユーザーフレンドリーです。したがって、「送信」ボタンを押すと、その画像は通常すでにアップロードされています。彼は「アップロードしています...」と言って画面を見ながら待つことはしません。

画像を取得する場合も同様です。特に携帯電話と限られたモバイルデータのおかげで、元の画像を送信したくない、サイズ変更された画像を送信したい、というわけではありません。まったくリサイズするために、あなたはあなたの視野にぴったり合うイメージを望みます)。このため、良いアプリはcloudinaryのようなものを使用しています(または、サイズ変更用の独自の画像サーバーを持っています)。

また、データが非公開でない場合は、URLだけをapp/frontendに送り返して直接クラウドストレージからダウンロードするので、帯域幅とサーバーの処理時間を大幅に節約できます。私たちのより大きなアプリケーションでは毎月たくさんのダウンロードされたテラバイトがあります、あなたはあなたのREST AP​​Iサーバのそれぞれで直接それを処理したくありません、それはCRUD操作に焦点を合わせられます。あなたはそれを一つの場所(私達のImageserver、キャッシュなどを持っている)で処理したいか、クラウドサービスにそれの全てを処理させたいです。


短所:あなたが考えるべき唯一の「短所」は「割り当てられていない画像」です。ユーザーは画像を選択して他のフィールドに入力し続けますが、彼は "nah"と言ってアプリやタブをオフにしますが、その間は画像のアップロードに成功しました。これは、どこにも割り当てられていない画像をアップロードしたことを意味します。

これを処理する方法はいくつかあります。最も簡単なのは、 "I don't care"です。これがあまり頻繁に起こらない場合、または(何らかの理由で)送信したすべての画像ユーザーを保存したいという希望がある場合は、関係ありません。削除。

もう1つの方法も簡単です - あなたは毎週CRONを持っています、そしてあなたは1週間以上古い未割り当ての画像をすべて削除します。

90
libik

いくつかの決定があります

  1. 最初のリソースパスについて

    • イメージをそれ自体でリソースとしてモデル化します。

      • ユーザーにネストされている(/ user /:id/image):ユーザーと画像の関係は暗黙的に作成されます

      • ルートパス(/ image)で:

        • クライアントは、イメージとユーザーの間の関係を確立する責任を負います。

        • セキュリティコンテキストが、イメージを作成するために使用されるPOST要求と共に提供されている場合、サーバーは認証されたユーザーとイメージの間の関係を暗黙的に確立することができます。

    • ユーザーの一部として画像を埋め込む

  2. 2番目の決定は、イメージリソースの表現方法についてです。

    • Base 64エンコードのJSONペイロードとして
    • マルチパートペイロードとして

これが私の決断です:

  • それが強いケースでない限り、私は通常パフォーマンスよりもデザインを好みます。それはシステムをより保守しやすくし、インテグレータによってより容易に理解することができます。
  • だから私の最初の考えはそれがあなたがすべてのJSONを保つことを可能にするので画像リソースのBase64表現に行くことです。このオプションを選択した場合は、リソースパスを好きなようにモデル化できます。
    • ユーザーと画像の関係が1対1の場合、両方のデータセットが同時に更新される場合は、画像を属性としてモデル化することをお勧めします。それ以外の場合は、イメージを属性として、PUTまたはPATCHを介して更新するか、別のリソースとして自由にモデル化することができます。
  • マルチパートペイロードを選択した場合は、イメージを独自のリソースとしてモデル化することを余儀なくされるので、他のリソース、この場合はユーザーリソースは、イメージにバイナリ表現を使用するという決定の影響を受けません。

base64 vs multipartの選択に関してパフォーマンスへの影響はありますか?。マルチパート形式でデータを交換する方が効率的なはずです。しかし、--- この記事 は、両方の表現のサイズの違いがほとんどないことを示しています。

私の選択Base64:

  • 一貫したデザイン決定
  • わずかなパフォーマンスへの影響
  • ブラウザはデータURI(base64でエンコードされた画像)を理解しているので、クライアントがブラウザであればこれらを変換する必要はありません。
  • 私はそれを属性リソースとするかスタンドアロンリソースとするかについて票を投じることはしません。それはあなたの問題の領域(私は知らない)とあなたの個人的な好みによって異なります。
77
Daniel Cerecedo

あなたの2番目の解決策はおそらく最も正しいです。意図したとおりにHTTP仕様とMIMEタイプを使用し、multipart/form-dataを介してファイルをアップロードする必要があります。関係を処理する限り、私はこのプロセスを使用します(私はあなたの仮定やシステム設計についてゼロを知っていることに留意してください)。

  1. ユーザーエンティティを作成するためのPOSTから/users
  2. POST画像を/imagesに変換します。HTTP仕様に従って、画像を取得できる場所にLocationヘッダーを必ず返すようにしてください。
  3. PATCH/users/carPhotoに変更して、ステップ2のLocationヘッダーに指定されている写真のIDを割り当てます。
9
mam8cc

簡単な解決策はありません。それぞれの方法にはそれぞれ長所と短所があります。しかし標準的な方法は最初のオプションmultipart/form-dataを使うことです。として W3お勧めガイド と言う

コンテンツタイプ "multipart/form-data"は、ファイル、非ASCIIデータ、およびバイナリデータを含むフォームを送信するために使用する必要があります。

実際にはフォームを送信していませんが、暗黙の原則が依然として適用されます。バイナリ表現としてbase64を使用するのは、目的を達成するために間違ったツールを使用しているために間違っています。一方、2番目のオプションは、APIサービスを消費するためにAPIクライアントにより多くの仕事をさせます。使いやすいAPIを提供するには、サーバー側で大変な作業をする必要があります。最初のオプションはデバッグが簡単ではありませんが、実行してもおそらく変更されることはありません。

multipart/form-dataを使うと、REST/httpの哲学に固執することになります。あなたは同様の質問に対する答えを見ることができます こちら

他の方法としては、multipart/form-dataを使用することができますが、すべての値を別々に送信する代わりに、payloadという名前の値と、その中にあるjsonペイロードを送信することができます。 (私はASP.NET WebAPI 2を使用してこの方法を試してみましたが、うまくいきました)。

2