Upload APIの設計を調査してみた

# 背景
ファイルアップロード用のWebAPIを設計するにあたって、いくつかのサービスを調査したのでまとめてみた

※今回は分割アップロードは考慮しない

# 調査結果

下記３種類に分かれる。(例外あり)
 - リクエストボディにそのまま入れる
 - `base64`へ変換
 - `multipart/form-data`にて送信

## リクエストボディにそのまま入れる
`Content-Type`にてファイルフォーマットを指定し、リクエストボディにデータをそのまま詰める

メリット：シンプル、分かりやすい  
デメリット：パラメータをどのように設定させるか検討する必要あり（HTTPヘッダー、URLパス）

### 事例
####  Microsoft(OneDrive, SharePoint Online)

ファイル自体はそのままリクエストボディに入れる。ファイル名はURLのパスを使って指定する

https://docs.microsoft.com/ja-jp/graph/api/driveitem-put-content?view=graph-rest-1.0&tabs=http

```
PUT /me/drive/root:/FolderA/FileB.txt:/content
Content-Type: text/plain

The contents of the file goes here.
```

#### Google Drive(Simple Upload)

Google Driveは`Simple Upload`と`Multipart Upload`の２種類が存在する（`resumable`は分割アップロードなので今回は対象外）が、`Simple Upload`の方式

ファイル名等のパラメータ設定はアップロード後に別のAPIを叩いて修正する

https://developers.google.com/drive/api/guides/manage-uploads#http

#### Dropbox

`Dropbox-API-Arg`というHTTPヘッダー内にパラメータを設定する

https://www.dropbox.com/developers/documentation/http/documentation#files-upload

## `base64`へ変換

ファイルを`base64`に変換して、リクエストを送る

メリット：JSON形式で送ることが出来る  
デメリット：`base64`に変換する分、ファイルサイズが大きくなる(下記リンク参考)

https://www.cisco.com/c/ja_jp/support/docs/security/email-security-appliance/118499-qa-esa-00.html

### 事例
####  Microsoft(Exchange Online)

JSONの`contentBytes`に`base64`に変換したファイルを入れて送信する。ファイル名等もJSONの別のキーに一緒に詰めて送信する

https://docs.microsoft.com/ja-jp/graph/api/message-post-attachments?view=graph-rest-1.0&tabs=http

```
POST https://graph.microsoft.com/v1.0/me/messages/AAMkpsDRVK/attachments
Content-type: application/json

{
  "@odata.type": "#microsoft.graph.fileAttachment",
  "name": "smile",
  "contentBytes": "R0lGODdhEAYEAA7"
}
```

#### SmartHR

JSON内に`base64`で変換したファイルを詰める。`multipart/form-data`を採用しなかった理由まで下記に書かれている

https://tech.smarthr.jp/entry/2017/02/13/182200


## `multipart/form-data`にて送信

`multipart/form-data`形式でファイル自体やパラメータを詰めて送信する。詳細は下記

https://qiita.com/sivertigo/items/14957200af5ad953d63b

メリット：データをそのまま送れる（`base64`変換のように容量が余計に大きくならない）
デメリット：他のAPIがJSON形式の場合、アップロードAPIだけ形式が変わってしまう

### 事例
#### Box

`multipart/form-data`形式で送信する。その他のパラメータは`attributes`という属性内にJSON形式で値を入れる

https://ja.developer.box.com/reference/post-files-content/


#### サイボウズ

`filename`にファイル名を入れて送信する（RFC準拠）

https://developer.cybozu.io/hc/ja/articles/202350470-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89API
```
//Sample
X-Cybozu-Authorization: cnlvX2Z1a3VkYTpjeWJvenU=
Authorization: Basic QWRtaW5pc3RyYXRvcjpjeWJvenU=
Content-Type: multipart/form-data; boundary=---------------------------bee48a285354
Content-Length: 215
 
-----------------------------bee48a285354
Content-Disposition: form-data; name="file"; filename="users.csv"
Content-Type: text/csv
 
User1,User1,User1,password
 
-----------------------------bee48a285354--
```

### 例外：`multipart/related`にて送信

Google Driveの`Multipart Upload`で利用されている

`multipart/form-data`のようにメタデータも一緒に送信することが可能  
ファイルアップロードAPIとしては、Google Drive以外ではあまり採用されていない

https://developers.google.com/drive/api/guides/manage-uploads#multipart

## まとめ
個人的な偏見も含まれているが、下記のようにまとめた。大きいサイズのファイルアップロードを必要とする場合は分割アップロードのAPIが必要になってくると思われるので、どれを選んでもそこまで間違った選択にはならないだろう。

 - リクエストボディにそのまま入れる
   - たくさんのパラメータ設定を必要としない場合
 - `base64`へ変換
   - JSON形式にしたい場合
 - `multipart/form-data`にて送信
   - 上記以外の場合

## 追加情報
関連記事に下記もありましたので、こちらもご覧ください。

https://qiita.com/mserizawa/items/7f1b9e5077fd3a9d336b