0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FlutterからSynology NASに画像をアップロードしようとしたらつまづいた話

Last updated at Posted at 2025-01-24

はじめに

FileStationAPIには、ファイルアップロードする機能(SYNO.FileStation.Upload)も提供されているのですが、自作のFlutterアプリから画像ファイルのアップロードを試みたところ、少し苦戦したのでその記録です。

httpパッケージでの試み

FlutterでREST APIにアクセスするとなると、一番有名なのがhttpパッケージかと思われます。
該当のAPIはPOSTメソッドなので、httpのpostを使ってアクセスしてみます。
リクエストボディのパラメーターについては以下のようになります。

パラメーター名 説明
path String アップロード先のFileStationのフォルダのパス
create_parents Boolean pathで指定したフォルダの親フォルダが存在しない時に作成するか
overwrite String("overwrite"か"skip") 同名のファイルがアップロード済みの場合、置き換えるかスキップするか
filepart バイナリ(何型?) ファイルデータをバイナリ化したもの

ではリクエストボディのデータをそれぞれ設定してアクセスしてみましょう。

final file = File('target_image.JPG');

// ファイルデータをバイナリとして読み込み
final fileData = file.readAsBytesSync();

final response = await http.post(
    Uri(
      scheme: 'http',
      host: '192.xxx.yyy.zzz',
      port: 5000,
      path: 'webapi/entry.cgi',
      queryParameters: <String, dynamic>{
        "api": "SYNO.FileStation.Upload",
        "version": "3",
        "method": "upload",
        "_sid": sid,         // ログインAPIで得られるsid
      },
    ),
    body: <String, dynamic>{
      "path": "/home",
      "create_parents": false,
      "overwrite": "overwrite",
      "filename": fileData,
    },
);

で、結果がこちら

200
{"error":{"code":401},"success":false}

上がHttp通信でのステータスコード、下が返却されたレスポンスボディです。
通信自体は成功してますが、内部で失敗してますね...

で、FileStationAPIにおけるエラーコード401は何を示すのかというと
スクリーンショット 2025-01-25 2.19.10.png
(API公式リファレンスより)

ファイル操作において不明なエラー
原因分かんないじゃん笑

まあ、十中八九設定したファイルデータに問題がありそうだなと思い、他のバイナリ形式に変更してみました。FileのreadAsBytesSyncメソッドでは「Uint8List」という型になります。
Dartではこれ以外にも「Base64」や「ByteData」など様々なバイナリ形式が提供されており、それぞれ相互変換できるようになっているため、色んなデータに変換して試してみたのですが、いずれの形式でも上記エラーから解消しませんでした。
困ったなぁと...

アップロードの成功例を探してみる

アップロードAPIが動作せず、途方に暮れていたのですが、このAPIがちゃんと動作している成功例を探すことに。
そこで見つけたのがFileStationAPIのPythonのラッパーパッケージでした。
github上に上げられており、pipでインストール可能なのでこれを使用してみることにしました。
アップロード処理はfilestationクラスのupload_fileメソッドから実行できるようです。

from synology_api import filestation

# FileStationのオブジェクト生成
fileStationApi = filestation.FileStation("192.xxx.yyy.zzz", "5000", "[ユーザー名]", "[パスワード]")

# アップロード処理
fileStationApi.upload_file(
    dest_path = "/home",
    file_path = "target_image.JPG",
    create_parents = False
)

上記コードを実行したところ...

Upload Progress: 100%|████████████████████████████| 7.99M/7.99M [00:03<00:00, 2.77MB/s]

プログレスバーがコンソール上に表示され、成功した模様ですね。
ではFileStationで確認すると
スクリーンショット 2025-01-25 4.05.04.png
アップロードされてますね!
というわけで、アップロードAPIでアップロード処理はちゃんとできるみたい。

では次に、アップロード処理のソースを見ていきます。

/synology_api/filestation.py(592〜597)
encoder = MultipartEncoder({
    'path': dest_path,
    'create_parents': str(create_parents).lower(),
    'overwrite': str(overwrite).lower(),
    'files': (filename, payload, 'application/octet-stream')
})

何やらリクエストボディ設定部分で「MultipartEncoder」なるものを使用している模様。
調べていると、どうやら画像ファイルをアップロードする際は今回のFileStationAPIに関係なく、基本的にContent-Typeを「multipart/form-data」というものにしなければならないそう。
先ほどの「MultipartEncoder」はリクエストボディの辞書型データをMultipartの形式にしてくれるもの(超ざっくり)だったようです。

Dioの使用

ではDartでMultipartを使うにはどうすれば良いのかなと調べていると、dioパッケージで実現できるとのこと。
こちらもhttpと同じくらい通信処理で使用されることが多いパッケージですね。
dioにはMultipartFileという、ファイルデータをMultipartにしてくれる機能が提供されているため、これを使えば実装できるはずです。
というわけで以下のように実装しました。

final dio = Dio();

// MultipartFileでファイルデータ生成
final fileData = await MultipartFile.fromFile('target_image.JPG');
final response = await dio.post(
    'http://192.xxx.yyy.zzz/webapi/entry.cgi',
    queryParameters: <String, dynamic>{
      "api": "SYNO.FileStation.Upload",
      "version": "3",
      "method": "upload",
      "_sid": sid,
    },
    // FormDataがリクエストボディに該当するためデータを設定
    data: FormData.fromMap(<String, dynamic>{
      "path": "/home",
      "create_parents": false,
      "overwrite": "overwrite",
      "filepart": fileData,
    }),
);

得られた結果がこちら

200
{"data":{"blSkip":false,"file":"target_image.JPG","pid":30875,"progress":1},"success":true}

httpの時と同様に上がHttp通信でのステータスコード、下が返却されたレスポンスボディです。
今回は通信と内部共に成功しているっぽいですね!

ではFileStationで確かめると
スクリーンショット 2025-01-25 3.40.17.png
ちゃんとアップロードされてますね!

というわけで、アップロードのAPIを使う場合はdioとMultipartFileを使用しないといけないというお話でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?