現在このURLを公開しているREST Webサービスがあります。
ユーザーは次のJSONをPOST
できます。
{
"Name": "Test",
"Latitude": 12.59817,
"Longitude": 52.12873
}
新しいメディアメタデータを作成するため。
今、私はメディアのメタデータと同時にファイルをアップロードする機能が必要です。これについて最善の方法は何ですか? file
という新しいプロパティを導入してbase64でファイルをエンコードすることはできましたが、もっと良い方法があるのではないかと思いました。
HTMLフォームが送信するもののようにmultipart/form-data
を使用することもありますが、私はREST Webサービスを使用しており、可能であればJSONを使用したいと思います。
私はGregに2フェーズアプローチが合理的な解決策であることに同意します、しかし私はそれを逆にします。私はします:
POST http://server/data/media
body:
{
"Name": "Test",
"Latitude": 12.59817,
"Longitude": 52.12873
}
メタデータエントリを作成して次のような応答を返すには
201 Created
Location: http://server/data/media/21323
{
"Name": "Test",
"Latitude": 12.59817,
"Longitude": 52.12873,
"ContentUrl": "http://server/data/media/21323/content"
}
その後、クライアントはこのContentUrlを使用してファイルデータでPUTを実行できます。
このアプローチのいいところは、サーバーが膨大な量のデータで重くなってきたときです。返されるURLは、より多くのスペース/容量を持つ他のサーバーを指すことができます。帯域幅が問題になる場合は、ある種のラウンドロビンアプローチを実装することもできます。
リクエスト本文全体をJSONでラップしていないからといって、1つのリクエストでJSONとファイルの両方をポストするためにmultipart/form-data
を使用するのはRESTfulではありません。
curl -F "metadata=<metadata.json" -F "[email protected]" http://example.com/add-file
サーバー側(擬似コードにPythonを使用)
class AddFileResource(Resource):
def render_POST(self, request):
metadata = json.loads(request.args['metadata'][0])
file_body = request.args['file'][0]
...
複数のファイルをアップロードするには、それぞれに別々の "フォームフィールド"を使用することも可能です。
curl -F "metadata=<metadata.json" -F "[email protected]" -F "[email protected]" http://example.com/add-file
...その場合、サーバーコードはrequest.args['file1'][0]
とrequest.args['file2'][0]
を持ちます。
または同じものを多くの人に再利用する
curl -F "metadata=<metadata.json" -F "[email protected]" -F "[email protected]" http://example.com/add-file
...その場合、request.args['files']
は単に長さ2のリストになります。
または実際には一度に複数のファイルを1つのフィールドに渡す:
curl -F "metadata=<metadata.json" -F "[email protected],some-other-file.tar.gz" http://example.com/add-file
...その場合、request.args['files']
はすべてのファイルを含む文字列になります。自分で解析する必要があります。その方法はわかりませんが、難しいことではない、または以前の方法を使用することをお勧めします。
@
と<
の違いは、@
がファイルのアップロードとして添付されるのに対し、<
はファイルの内容をテキストフィールドとして添付することです。
PScurl
リクエストを生成する方法としてPOST
を使用しているからといって、まったく同じHTTPリクエストを送信できないわけではありません。 Pythonなどのプログラミング言語、または十分に有能なツールを使用している。
この問題に対処する1つの方法は、アップロードを2段階のプロセスにすることです。まず、POSTを使用してファイル自体をアップロードします。この場合、サーバーは何らかの識別子をクライアントに返します(識別子はファイルの内容のSHA1かもしれません)。次に、2番目のリクエストでメタデータをファイルデータに関連付けます。
{
"Name": "Test",
"Latitude": 12.59817,
"Longitude": 52.12873,
"ContentID": "7a788f56fa49ae0ba5ebde780efe4d6a89b5db47"
}
JSON要求自体にエンコードされたファイルデータベースbase64を含めると、転送されるデータのサイズが33%増加します。ファイルの全体的なサイズによっては、これが重要になる場合とそうでない場合があります。
もう1つの方法は、生ファイルデータのPOSTを使用することですが、HTTP要求ヘッダーにメタデータを含めます。ただし、これは基本的なREST操作からは少し外れているため、一部のHTTPクライアントライブラリでは面倒な場合があります。
私はこれが非常に古い質問であることを認識しています、しかし私が同じ事を捜してこの記事に来たとき、これが他の誰かを助けるのを願っています。私のメタデータがGuidとintであるということだけで、私は同様の問題を抱えていました。解決策は同じです。必要なメタデータをURLの一部にすることができます。
あなたの "Controller"クラスのPOST受付メソッド:
public Task<HttpResponseMessage> PostFile(string name, float latitude, float longitude)
{
//See http://stackoverflow.com/a/10327789/431906 for how to accept a file
return null;
}
あなたがルートを登録しているものは何でも、この場合私のためにWebApiConfig.Register(HttpConfiguration config)を。
config.Routes.MapHttpRoute(
name: "FooController",
routeTemplate: "api/{controller}/{name}/{latitude}/{longitude}",
defaults: new { }
);
ファイルとそのメタデータが1つのリソースを作成している場合は、1つの要求で両方をアップロードしても問題ありません。サンプルリクエストは次のようになります。
POST https://target.com/myresources/resourcename HTTP/1.1
Accept: application/json
Content-Type: multipart/form-data;
boundary=-----------------------------28947758029299
Host: target.com
-------------------------------28947758029299
Content-Disposition: form-data; name="application/json"
{"markers": [
{
"point":new GLatLng(40.266044,-74.718479),
"homeTeam":"Lawrence Library",
"awayTeam":"LUGip",
"markerImage":"images/red.png",
"information": "Linux users group meets second Wednesday of each month.",
"fixture":"Wednesday 7pm",
"capacity":"",
"previousScore":""
},
{
"point":new GLatLng(40.211600,-74.695702),
"homeTeam":"Hamilton Library",
"awayTeam":"LUGip HW SIG",
"markerImage":"images/white.png",
"information": "Linux users can meet the first Tuesday of the month to work out harward and configuration issues.",
"fixture":"Tuesday 7pm",
"capacity":"",
"tv":""
},
{
"point":new GLatLng(40.294535,-74.682012),
"homeTeam":"Applebees",
"awayTeam":"After LUPip Mtg Spot",
"markerImage":"images/newcastle.png",
"information": "Some of us go there after the main LUGip meeting, drink brews, and talk.",
"fixture":"Wednesday whenever",
"capacity":"2 to 4 pints",
"tv":""
},
] }
-------------------------------28947758029299
Content-Disposition: form-data; name="name"; filename="myfilename.pdf"
Content-Type: application/octet-stream
%PDF-1.4
%
2 0 obj
<</Length 57/Filter/FlateDecode>>stream
x+r
26S00SI2P0Qn
F
!i\
)%[email protected]
[
endstream
endobj
4 0 obj
<</Type/Page/MediaBox[0 0 595 842]/Resources<</Font<</F1 1 0 R>>>>/Contents 2 0 R/Parent 3 0 R>>
endobj
1 0 obj
<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding/WinAnsiEncoding>>
endobj
3 0 obj
<</Type/Pages/Count 1/Kids[4 0 R]>>
endobj
5 0 obj
<</Type/Catalog/Pages 3 0 R>>
endobj
6 0 obj
<</Producer(iTextSharp 5.5.11 2000-2017 iText Group NV \(AGPL-version\))/CreationDate(D:20170630120636+02'00')/ModDate(D:20170630120636+02'00')>>
endobj
xref
0 7
0000000000 65535 f
0000000250 00000 n
0000000015 00000 n
0000000338 00000 n
0000000138 00000 n
0000000389 00000 n
0000000434 00000 n
trailer
<</Size 7/Root 5 0 R/Info 6 0 R/ID [<c7c34272c2e618698de73f4e1a65a1b5><c7c34272c2e618698de73f4e1a65a1b5>]>>
%iText-5.5.11
startxref
597
%%EOF
-------------------------------28947758029299--
8年の間に誰も簡単な答えを投稿していないのはなぜでしょうか。ファイルをbase64としてエンコードするのではなく、jsonを文字列としてエンコードします。その後、サーバー側でJSONをデコードするだけです。
Javascriptでは:
let formData = new FormData();
formData.append("file", myfile);
formData.append("myjson", JSON.stringify(myJsonObject));
Content-Type:multipart/form-dataを使用してPOSTします。
サーバー側では、ファイルを通常どおりに取得し、jsonを文字列として取得します。文字列をオブジェクトに変換します。これは、使用するプログラミング言語に関係なく、通常1行のコードです。
(はい、それは素晴らしい仕事をします。私のアプリの1つでそれをやってください。)