Jupyter NotebookのWebAPIを用いたファイルのアップロード/ダウンロード

概要

ここでは、Jupyter NotebookのWebAPIを用いたファイルのアップロード/ダウンロード方法を記載しています。

実行例・実装例のフォルダ構成

実行例のフォルダ構成を下記に示します。

/ <Jupyter Notebookのルートディレクトリ>
|- test_file
|- test_folder
    |- test_folder_file_1.ipynb
    |- test_folder_file_2.ipynb

リクエスト送信時に必要な値の取得

  • <Jupyter NotebookのURL>

    AAPF WebUIに表示されている[Access Jupyter Notebook] ボタンからJupyter Notebookにアクセスしてください。 利用しているブラウザのアドレスバーに表示されているURLをコピーし、下記のように .../jupyter で 終わるように修正した上で使用してください。

    http://web.example.com/proxy/a2b69b7d848324ee19b25278d29d45b4/jupyter
    

    このURLはAAPF WebAPIを利用して取得することもできます。詳しくは、 AAPF WebAPI Reference を参照してください。

  • <アップロード先・ダウンロード元のパス>

    Jupyter Notebook上のファイルパスを指定する際は、 Jupyter Notebookのルートディレクトリからの相対パスを指定してください。

    下図の矢印が示すディレクトリがJupyter Notebookのルートディレクトリです。

    _images/home_directory.png

    上記の画像の、 test_folder_file_1.ipynb を指定するパスは以下となります。:

    /api/contents/test_folder/test_folder_file_1.ipynb
    
  • <Jupyter Token>

    Jupyter TokenはAAPF WebUIから取得します。使用したいAAClusterの [Access Jupyter Notebook] ボタンをクリックし、 表示されるJupyter Tokenを使用してください。

    Jupyter TokenはAAPF WebAPIを利用して取得することもできます。詳しくは、 AAPF WebAPI Reference を参照してください。

Tip

使用方法の実行例には、Jupyter NotebookのWebAPI呼び出しに curl コマンドを使用しています。 curl コマンドのインストールについては、 https://curl.haxx.se/download.html を参照してください。

ファイルのアップロード方法

25MB未満のファイルのアップロード

25MB未満のファイルは PUT /api/contents/ を使うことでアップロードできます。 ファイルの内容はリクエスト送信時にJSONパラメータの一部としてリクエストボディに入れます。

実行例

ここでは bash を用いた実行例を記載しています。

$ curl -X PUT \
    -H "Accept: application/json" \
    -H "Authorization: Token c1663431be5df1873505524d720f958aa1fecbfd9fc06123" \
    -d @- \
    "http://web.example.com/proxy/a2b69b7d848324ee19b25278d29d45b4/jupyter/api/contents/test_folder/upload_test.txt" << EOF
    {
        "content": "$(base64 upload_test.txt)",
        "type": "file",
        "format": "base64"
    }
    EOF

リクエストパラメータの指定方法

Jupyter Tokenをリクエストヘッダーに設定します。

-H "Authorization: Token <Jupyter Token>" \

リクエストボディとしてJSON形式のデータを送信します。内容は以下の構造で生成してください。

{
    "content": "$(base64 <アップロード元のパス>)",
    "type": "file",
    "format": "base64"
}
  • content
    Base64を使用してテキストにエンコードされたファイルの内容を設定します。 この例では、 base64 コマンドを使って <アップロード元のパス> にある ファイルの内容をエンコードしています。

アップロード先のパスをURLに含めて指定します。

<Jupyter NotebookのURL>/api/contents/<アップロード先のパス>

Note

アップロード時に存在しないディレクトリにファイルをアップロードすることはできません。 アップロード先のディレクトリは事前に作成してください。

25MB以上のファイルのアップロード

データ量が25MB以上のファイルは、1MBごとに分割してアップロードしてください。 また、この方法は25MB以下のファイルでもアップロードができます。

Hint

ディレクトリをまとめてアップロードする場合は、 zip 等で圧縮したファイルをアップロードしてください。

実装例

ここではPythonで実装した例を記載しています。

import requests
import base64
import json
import os
import sys

# ファイルアップロード時に必要となる値を入れる変数です
upload_from = 'upload_test.txt'
jupyter_path = 'http://web.example.com/proxy/a2b69b7d848324ee19b25278d29d45b4/jupyter'
jupyter_token = 'c1663431be5df1873505524d720f958aa1fecbfd9fc06123'
upload_to = 'test_folder/upload_test.txt'

with open(upload_from, 'rb') as file:

    file_name = os.path.basename(upload_from)
    chunk_size = 1024 * 1024

    headers = {'Content-Type': 'application/json',
               'Authorization': 'token {}'.format(jupyter_token)}

    # ファイルを分割して読み込むメソッドです
    def divide_file():
        chunk = 0
        read_data_size = 0

        # 読み込んだファイルの容量がファイルサイズと一致するまで処理を実行させます
        content_length = os.path.getsize(upload_from)
        while read_data_size < content_length:
            content = file.read(chunk_size)
            encoded_content = base64.b64encode(content).decode('utf-8')

            # 読み込んだデータサイズの更新をします
            read_data_size = read_data_size + len(content)

            # ファイルが最後まで読み込まれた際に、`chunk`は`-1`とします
            if read_data_size == content_length:
                chunk = -1
            else:
                chunk += 1

            # リクエストのボディとなるように返却します
            yield {
                'content': encoded_content,
                'format': 'base64',
                'name': file_name,
                'path': upload_to,
                'type': 'file',
                'chunk': chunk
            }

    try:
        # ファイルを読み終わるまで繰り返す処理とします
        for payload in divide_file():
            # 作成したリクエストをJupyterに送信します
            requests.put('{}/api/contents/{}'.format(jupyter_path, upload_to),
                    headers=headers, data=json.dumps(payload))
    except:
        # 例外処理を記載します
        print("Unexpected error:", sys.exc_info()[0])
        raise

Tip

Jupyter Notebookへリクエストを送信するツールとして、 PyPIで公開されているパッケージの requests を使用しています。

実装内容

  1. 送信するファイルを分割し、アップロードが可能な状態にします。

    1. ファイルを1MBごとに読み込みます。
    2. 1.で読み込んだファイルを Base64 エンコードします。
  2. 分割したファイルごとに PUT リクエストを送信します。

    1. リクエストごとに chunk という番号を 1 から順につけ、最後のリクエストには -1 をつけます。

    2. ヘッダーに以下を設定します。
      {
          "Content-Type": "application/json",
          "Authorization": "token <Jupyter Token>"
      }
      
    3. リクエストボディの内容を生成します。
      {
          "content": <分割したファイルの内容>
          "format": "base64",
          "name": <ファイル名>,
          "path": <アップロード先のパス>,
          "type": "file",
          "chunk": <2-1. で付けた番号>
      }
      
    4. 下記のURLへ、 PUT で各リクエストを順に送信します。
      <Jupyter NotebookのURL>/api/contents/<アップロード先のパス>
      

ファイルのダウンロード方法

ファイルのダウンロードは GET /files/ を使用します。

実行例

ここでは、bashを用いてダウンロードしたファイル内容を リダイレクションにてファイル内へ書き込む例を、記載しています。

$ curl -X GET \
    -H 'Accept: application/json' \
    -H 'X-Response-Encoding: chunked' \
    -H "Authorization: Token <Jupyter Token>" \
    "<Jupyter NotebookのURL>/files/<ダウンロード元のパス>" \
    > <ダウンロード先のパス>

リクエストパラメータ

Jupyter Tokenをリクエストヘッダーに設定します。

-H "Authorization: Token <Jupyter Token>" \

ダウンロード元のパスをURLに含めて指定します。

<Jupyter NotebookのURL>/files/<ダウンロード元のパス>

Hint

GET /files/ では Range ヘッダーが使用可能です。このヘッダーを使用することで、 ファイルの一部分をダウンロードしたり、ファイルダウンロードが失敗した際のリジュームを行うことができます。