REST APIでファイルをアップロード

RESTアップロードを初期化し、単一転送または再開可能なマルチパート転送でバイトを送信して保存済みファイルを確認し、制限、競合、レスポンスの喪失から安全に復旧します。

最終確認日

スクリーンショットは英語版の画面です。

REST APIでファイルをアップロードするには、アップロードセッションを開き、バイトを送信して、最終的なファイルのレスポンスを読み取ります。初期化レスポンスは転送を予約するもので、保存済みファイルが使用可能になったことを意味しません。小さいファイルは1回のPUTで完了し、マルチパートアップロードには別途完了の呼び出しが必要です。

認証情報と保存先

プロフィール → APIキーを開き、API:APIキーを作成を選択してアップロードのみを選びます。次に制限と有効期限 → プロトコルを開き、選択したもののみを選んでREST APIを確認します。キーを生成から取得した一度限りのトークンは安全に保存してください。ワークスペースの固定には対応していますが、通常のキーのフォルダーアンカーはRESTでは対応していません。

保存先のworkspace_idと、サブフォルダーの場合はfolder_idが必要です。アップロード専用キーでアップロードセッションの開始と状態確認はできますが、通常のファイルやワークスペースの一覧は取得できません。別途許可された読み取り用認証情報か、サインイン済みアプリのリクエストからIDを取得してください。保存先を調べるためだけに本番環境のアップロードキーの権限を一時的に広げないでください。

許可された読み取りキーでIDを調べるには、DOSYA_READ_API_KEYを安全に指定してワークスペースの一覧をリクエストします。

curl --fail-with-body --silent --show-error \
  'https://api.dosya.dev/api/workspaces' \
  -H "Authorization: Bearer $DOSYA_READ_API_KEY"

workspacesから目的のワークスペースのidをコピーします。次に同じ読み取り権限でGET /api/files?workspace_id=THAT_IDをリクエストしてルートの一覧を確認し、返されたフォルダーから保存先フォルダーのIDをコピーします。入れ子のフォルダーを確認するにはfolder_idも指定します。これらのIDをアップロード用認証情報とともに使用してください。人が読める名前はAPIのIDではありません。

ワークスペースのメンバーシップでupload_filesとアップロードページへのアクセスが許可されている必要があります。保存先はメンバーに設定された範囲内にあり、そのメンバーから見えて、新しいコンテンツを受け入れる必要があります。フォルダーやファイルのロック、拡張子のルール、オーナーのストレージ容量の上限、メンバーのストレージ上限、ワークスペースの同時実行設定によって転送が停止することがあります。

「REST APIでファイルをアップロード」の手順。アップロードを作成し、バイトを転送して完了した後、返されたファイルが対象フォルダーに表示されることを確認します。
「REST APIでファイルをアップロード」の手順。アップロードを作成し、バイトを転送して完了した後、返されたファイルが対象フォルダーに表示されることを確認します。 スクリーンショットを原寸大で開く。

手順:curlで小さなテストファイルをアップロード

DOSYA_API_KEYを安全に指定し、ワークスペースのプレースホルダーを実際に許可されたIDに置き換えてください。この例にはcurlとPython 3が必要です。12バイトのファイルを作成し、ワークスペースのルートにアップロードします。

set -e
printf 'Hello dosya\n' > dosya-api-test.txt
DOSYA_WORKSPACE_ID='REPLACE_WITH_WORKSPACE_ID'
python3 - "$DOSYA_WORKSPACE_ID" <<'PYTHON' > upload-request.json
import json, sys
print(json.dumps({"workspace_id": sys.argv[1],
                  "file_name": "dosya-api-test.txt", "file_size": 12,
                  "mime_type": "text/plain"}))
PYTHON
curl --fail-with-body --silent --show-error \
  'https://api.dosya.dev/api/upload/init' \
  -H "Authorization: Bearer $DOSYA_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @upload-request.json -o upload-init.json
DOSYA_UPLOAD_PATH=$(python3 -c 'import json; print(json.load(open("upload-init.json"))["upload_url"])')
curl --fail-with-body --silent --show-error --request PUT \
  "https://api.dosya.dev$DOSYA_UPLOAD_PATH" \
  -H "Authorization: Bearer $DOSYA_API_KEY" \
  -H 'Content-Type: text/plain' \
  --data-binary @dosya-api-test.txt -o upload-result.json
python3 -m json.tool upload-result.json

初期化に失敗した場合は停止し、以前の試行の古いパスでPUTを実行しないでください。保存先フォルダーが分かっている場合は初期化JSONに"folder_id": "REPLACE_WITH_FOLDER_ID"を追加します。名前からパス区切り文字、パストラバーサル、制御文字を取り除いてから、ファイル名の上限である255文字に照らして確認します。file_sizeにはMBや書式付き文字列ではなく、実際のバイト数を整数で送信してください。

初期化は201とともにsession_id、upload_url、workspace_id、file_name、file_size、mime_type、extension、region、resumableを返します。**52,428,800バイト(50 MiB)**以下のファイルでは、force_multipart: trueを指定しない限りresumableはnullです。リージョンはワークスペースから決まり、クライアントがリージョンを指定してもファイルは移動しません。

PUTの201レスポンスにはok: trueとfileが含まれ、id、name、size_bytes、version、region、content_hash、hash_verified、etagが含まれます。size_bytesをローカルファイルと照合し、IDを保持してください。アップロード専用キーではセッションのステータスから完了を確認できます。結果のファイルを個別に確認するには、サインイン済みのファイル一覧か読み取りキーを使用します。アップロードだけでは公開リンクの作成や受信者へのメール送信は行われません。

マルチパート転送と再開

50 MiBを超える場合は返されたresumableの値を使用します。パートは**10,485,760バイト(10 MiB)**で、最後のパートだけは短くなります。total_partsは宣言サイズをパートサイズで割り、切り上げた値です。0より大きいサイズのファイルではforce_multipart: trueを指定することもできます。0バイトでマルチパートを強制すると拒否されます。

より大きなローカルファイルでは、12バイトのテスト値をそのまま使わず、実際のサイズから新しい初期化ボディを作成します。次のPythonコマンドは選択済みのワークスペースを使ってupload-request.jsonを置き換えます。必要に応じてpathを変更し、分かっているfolder_idを追加してください。

python3 - "$DOSYA_WORKSPACE_ID" <<'PYTHON' > upload-request.json
import json, sys
from pathlib import Path
path = Path("REPLACE_WITH_LOCAL_FILE_PATH")
print(json.dumps({"workspace_id": sys.argv[1], "file_name": path.name,
                  "file_size": path.stat().st_size,
                  "mime_type": "application/octet-stream"}))
PYTHON

先ほどのPOST /api/upload/initをそのボディで実行し、レスポンスをupload-init.jsonに保存して、以下の再開可能なプログラムで変更されていない同じローカルファイルを使用します。中断時に同じセッションを再開できるよう、成功した初期化JSONを保持してください。次のPython 3プログラムは不足パートを順番に送信し、完了します。resume-upload.pyとして保存し、非公開の環境変数DOSYA_API_KEYを設定してpython3 resume-upload.py PATH_TO_UNCHANGED_FILE upload-init.jsonを実行します。resumableがnullでない初期化が必要です。

import json, os, sys
from pathlib import Path
from urllib.request import Request, urlopen

base = "https://api.dosya.dev"
init = json.loads(Path(sys.argv[2]).read_text())
r = init["resumable"]
if r is None:
    raise SystemExit("Use the single PUT flow for this session")
file = Path(sys.argv[1])
if file.stat().st_size != init["file_size"]:
    raise SystemExit("Local size changed; open a new upload session")
headers = {"Authorization": "Bearer " + os.environ["DOSYA_API_KEY"]}

def call(path, method="GET", body=None):
    req = Request(base + path, data=body, method=method,
                  headers={**headers, "Content-Type": "application/octet-stream"})
    with urlopen(req) as response:
        return json.load(response)

state = call(r["status_url"])
if state["status"] == "complete":
    print("Already complete; file:", state["file_id"])
    raise SystemExit(0)
if state["status"] == "failed":
    raise SystemExit("Session failed; open a fresh session and resend the file")
done = set(state["uploaded_parts"])
with file.open("rb") as stream:
    for number in range(1, r["total_parts"] + 1):
        chunk = stream.read(r["part_size"])
        if number not in done:
            call(r["part_upload_url"] + "/" + str(number), "PUT", chunk)
result = call(r["complete_url"], "POST", b"")
print("Stored file:", result["file"]["id"], "bytes:", result["file"]["size_bytes"])

ローカルファイルを変更しないでください。サイズが同じでも内容が同じとは限りません。パート番号は1から始まり、total_partsを超えてはいけません。記録済みパートを再試行すると、保存済みのETagとalready_uploaded: trueが返されます。そのパートのバイトは置き換えられません。HTTP 413とPart exceeds the 10 MB part sizeはチャンクが大きすぎることを示します。空のパートはEmpty part dataとして拒否されます。

完了リクエストはcomplete_urlへのPOSTです。パートはサーバーがすでに追跡しているため、クライアントのETag配列は不要です。パートが不足している場合はOnly N of M parts uploaded. Upload remaining parts before completing.と返されます。ステータスを確認し、不足番号を送信してから再度完了してください。初期化を冪等な操作として再送しないでください。別のセッションが開かれ、このエンドポイントを保護するIdempotency-Keyヘッダーはありません。

タイムアウト、競合、安全な復旧

GET /api/upload/{session_id}/statusはstatus、bytes_uploaded、uploaded_parts、part_size、total_parts、has_multipart、file_idを返します。file_idは完了までnullです。最終レスポンスが失われた場合は、まずこのエンドポイントを確認してください。completeセッションとそのIDを使って、別のバージョンをアップロードせずに復旧できます。

一度も開始されていない保留中のセッションは、操作がないまま10分が経過すると削除されることがあります。開始済みのマルチパートセッションは、書き込みが届かないまま24時間が経過すると削除対象になります。クリーンアップの時刻は正確な有効期限を保証するものではありません。削除されたセッションは新たに初期化して再開し、すべてのパートを再送してください。放棄された認証情報の同時実行枠は、更新されなければ15分後に期限切れになります。パートリクエストはアクティブなアップロードの枠を更新します。

エラー対処
403、You don't have upload permission in this workspaceワークスペース管理者に必要な操作の許可を依頼するか、許可された保存先を選択します。
403、You don't have access to the upload pageRESTアップロードには、アップロード権限に加えてページへのアクセスがロールに必要です。
404、Destination folder not foundIDがこのワークスペースに属し、表示可能であることを確認します。フォルダー名で代用しないでください。
403、folder_locked再試行前に権限のある管理者とともに保存先のロックを解除します。
400、File exceeds the … GB size limit for this workspaceより小さいファイルを使うか、管理者にワークスペースのルールを変更してもらいます。
認証情報のファイルサイズ上限による413より大きな上限が許可されている場合にのみキーを交換します。サーバーは実際の受信バイト数も確認します。
400とerror_code: concurrent_upload_limit他の転送を完了またはキャンセルし、レスポンスのmax_concurrent_uploadsを確認します。
409、Upload session is already completeステータスを確認し、保存済みファイルのIDを復旧します。
429または再試行可能な503Retry-Afterに従い、マルチパートの状態を保持して不足パートを再試行します。並行して代替セッションを開かないでください。

ストレージ、サイズと拡張子、メンバーごとの上限、同時アップロードの確認は、保存先とバージョンの検証より先に行われます。完了時には受信バイト数に対して適用される上限が再確認されるため、初期化に通過しても完了処理の成功は保証されません。

同名のアップロードはアクセス権が許す場合、既存ファイルの新しいバージョンになることがあります。アップロードは常に新規作成だけを行うわけではありません。意図的に置き換える場合は、初期化時にfile_idと最後に確認したexpected_versionを送信します。古いバージョンでは409 version_conflictが返されます。更新して、新しいデータで置き換えるか判断してください。意図しないバージョン変更を避けるには、一意のテストファイル名を使用します。ワークスペースのmax_versions保持ルールにより、置き換え成功後に古いバージョンのオブジェクトが完全に削除されることがあります。設定がない場合の初期値は保持するバージョン10件です。すべてのアップロードで履歴が無制限に保持されると考えず、必要な古いリビジョンは置き換え前に保存してください。

未完了のセッションを破棄するには、同じ認証情報でDELETE /api/upload/{session_id}を送信します。途中までのストレージへの書き込みを中止してセッションを終了します。存在しないセッションや失敗したセッションにはalready_gone: trueとともにok: trueが返されます。完了済みセッションは409を返し、このキャンセルエンドポイントでは削除されません。キャンセルすると保存されていない転送の進捗は破棄されますが、完了したファイルやバージョンは取り消されません。

単一PUTでは、期待するSHA-256を正確に64文字の十六進数で記載したX-Dosya-Sha256を任意で送信できます。ダイジェストが一致するとhash_verified: trueが設定されます。不一致の場合は期待ハッシュと受信ハッシュとともに400 hash_mismatchが返されます。このヘッダーがない場合のfalseはクライアントのダイジェストが指定されなかったことを示し、検証失敗を意味しません。マルチパートの完了では同じ一括アップロードのハッシュ保証はありません。整合性が不可欠な場合はダウンロードしたバイトを別途検証してください。

デバッグトレースにトークンを含めず、セッションのメタデータは安全に保存してください。失われたトークンは、マスクされた一覧の行から復旧できません。漏えいした認証情報は無効化してください。アップロード認証ヘッダーをサポートに送信しないでください。

期待される結果

完了したセッションは、選択したワークスペースとフォルダーに永続的なファイルを示します。返されたバイト数は受信したボディを反映します。同名の既存ファイルがある場合は、新しいバージョンになることがあります。転送を成功とする前にID、サイズ、バージョンを確認してください。

関連ガイド

この記事は役に立ちましたか?

評価の結果を読み込んでいます…

投票すると、回答を変更できるように、必要な投票用Cookieが最長180日間保存されます。

質問と回答

このガイドの手順について質問できます。質問は確認後に公開されます。

公開の質問には、アカウントの詳細、認証情報、非公開ファイルを含めないでください。これらについては 非公開サポート をご利用ください。

質問を読み込んでいます…

    dosyaアカウントにサインインすると、質問を投稿できます。

    やり直す前に結果を確認

    操作をもう一度確認する必要があるときは、次の共通の確認事項をご利用ください。