ArweaveへのアップロードをCLIで行う (ArDrive CLI版)
IPFSからArweaveへ乗り換え
最近IPFSの代わりにArweaveを使う人が増えてきたように思います。
自分も元々NFTの画像とメタデータの保存に当初IPFSを利用していましたが、Pinataなどのマネージドのサービスを利用すると毎月$20かかるなどストレージとしてのコスパが悪いと感じていました。
単純にS3やGCSも検討しましたが、気持ち的に片足Web3に突っ込んでおきたいなということでArweaveを利用することにしました。
軽く調べたところArConnectとArDriveがシンプルで使い勝手が良さそうだったのでこの2つを使っています。
Arweaveのサービス:ArConnect / ArDrive
ArConnectはブラウザ拡張機能のウォレットです。
よくあるウォレットと似たような感じでそこまで違和感なく使用が可能です。
ArDriveはGUI上の操作で簡単にArweaveへファイルがアップロードできるサービスです。
フォルダ分けやリネームなどIPFSと比較すると非常に使いやすいです。
一度アップロードしたファイルは消せないという面はありますが、フォルダ分けや移動させることができるので最終Archiveフォルダ的なものを作成しておけばファイルが邪魔になることもありません。
ArDriveのCLIで大量ファイルをアップロード
NFTプロジェクトなど大量のファイルを扱うときにWebサービスだとブラウザが固まったりするため、CLIでの操作が必要となります。
Arweaveに対してのCLIのツールはいくつかあるようですが、ArDrive自体もCLIツールを出しているので今回はそちらを利用することにしました。
作業手順的には以下の手順になります。
- Walletのjsonファイル準備
- ArDrive CLIのインストール
- ファイルのアップロード
- マニュフェストの作成
1. Walletのjsonファイル準備
ArConnectの設定画面 > Download Config からjsonファイルのダウンロードが可能です。
configファイルとjsonファイルそれぞれダウンロードができますが、今回利用するのはjsonファイルになります。
CLI上でパスを指定して利用するため、利用しやすいフォルダに配置しておくと良いです。
2. ArDrive CLIのインストール
利用方法は簡単でnpmでglobalにインストールしたら即使えます。
npm install -g ardrive-cli
# then invoke the CLI from anywhere on your system:
ardrive
コマンドに関してはGithubのReadmeを見ると良いですが、動作確認も込めた基本操作を載せておきます。
$ ardrive folder-info --folder-id "dc349635-c426-4f8e-b03d-f5a208c179fc"
{
"appName": "ArDrive-App",
"appVersion": "1.39.0",
"arFS": "0.11",
"contentType": "application/json",
"driveId": "1cf5a39f-8999-4c70-a69b-ac3ca8b96eab",
"entityType": "folder",
"name": "ArDriveCLITest",
"txId": "iF59iRJbh1_S2Xppke1NacmUxIoSG29Zw5SrvlC6jSc",
"unixTime": 1669212319,
"customMetaDataGqlTags": {
"App-Platform": "Web"
},
"parentFolderId": "a8ff1c8f-a733-4afb-a02a-cb670bea29fa",
"entityId": "dc349635-c426-4f8e-b03d-f5a208c179fc",
"folderId": "dc349635-c426-4f8e-b03d-f5a208c179fc"
}
フォルダIDだけ少し探したのですが、ブラウザのURLから確認が可能です。
詳細に表示されているID系は全てフェイクです。
3. ファイルのアップロード
ファイルのアップロードですが、ファイル単位でもフォルダ単位でもアップロードが可能です。
フォルダ単位でアップロードする場合は以下のコマンドで可能です。
ardrive upload-file --local-path ./metadata --parent-folder-id "dc349635-c426-4f8e-b03d-f5a208c179fc" -w ./wallet.json
{
"created": [
{
"type": "folder",
"entityId": "7f6e81ae-c0da-4728-a047-d09574cf8a02",
"metadataTxId": "CD13St_e1nRhDgNp7a-07c1k7ETtydEnk5q68Du0csw",
"bundledIn": "mvprJ3C3pLIdqEzHnbMnd3v2g3YNMMDTvCdKEXxewiE",
"entityName": "metadata",
"sourceUri": "file:///xxxx/Sandbox/metadata"
},
{
"type": "file",
"entityName": "0.json",
"entityId": "10f49d1a-877c-43c5-8ee8-e263206d0e2e",
"dataTxId": "4FuinPa6xO2OiS7BffZdnOraRl__wxL4mq3Vb6z496g",
"metadataTxId": "qDUbc4mDByX1fk5IZd4iR2KzwAYB4tjOsJWKTDpW0eg",
"bundledIn": "mvprJ3C3pLIdqEzHnbMnd3v2g3YNMMDTvCdKEXxewiE",
"sourceUri": "file:///xxxx/Sandbox/metadata/0.json"
},
{
"type": "file",
"entityName": "1.json",
"entityId": "66334dec-5e17-4ef0-9dfd-058694186698",
"dataTxId": "UZz6Wb710T-M_M0mWbnukc2j_GNFHcw8fBFaSw8re2s",
"metadataTxId": "u8Qp4A_6Bx88APclpLZJ8KMK4IxAaTbI6U9Fe1SzaGU",
"bundledIn": "mvprJ3C3pLIdqEzHnbMnd3v2g3YNMMDTvCdKEXxewiE",
"sourceUri": "file:///xxxx/Sandbox/metadata/1.json"
},
{
"type": "file",
"entityName": "2.json",
"entityId": "47f4935f-60db-4a24-a555-3fa142714970",
"dataTxId": "CCxyxom6h37PZ7iDBQDd6myzav9-U7r63LuJpefq74E",
"metadataTxId": "ZRELrf74uPWQHtTT9DIVJ0WIgd_3YSkwd1Bpf6wJHTA",
"bundledIn": "mvprJ3C3pLIdqEzHnbMnd3v2g3YNMMDTvCdKEXxewiE",
"sourceUri": "file:///xxxx/Sandbox/metadata/2.json"
},
{
"type": "file",
"entityName": "3.json",
"entityId": "7d6b3ba9-9b11-41ff-8f93-42365bff1229",
"dataTxId": "tAdhTNY4RQNDlMzPTDj6Tdq9w_TYebvrSC61hORDL0E",
"metadataTxId": "gw4C7vFxaYEGjYAa8LB-gaweQ6_m_Too6TdkWSvcKCU",
"bundledIn": "mvprJ3C3pLIdqEzHnbMnd3v2g3YNMMDTvCdKEXxewiE",
"sourceUri": "file:///xxxx/Sandbox/metadata/3.json"
},
{
"type": "file",
"entityName": "4.json",
"entityId": "55b8fa68-bcf9-414b-a44b-e59e634b1e9f",
"dataTxId": "FLJ2jixnae-WMqIkZ6zb7I4ScL9L-W3ta66SuFe3OMU",
"metadataTxId": "yp_bq1HsSW-6PFAWAMOm0q5bW8fMNgUgQyOfSsahjNs",
"bundledIn": "mvprJ3C3pLIdqEzHnbMnd3v2g3YNMMDTvCdKEXxewiE",
"sourceUri": "file:///xxxx/Sandbox/metadata/4.json"
},
{
"type": "bundle",
"bundleTxId": "mvprJ3C3pLIdqEzHnbMnd3v2g3YNMMDTvCdKEXxewiE"
}
],
"tips": [
{
"recipient": "Zznp65qgTIm2QBMjjoEaHKOmQrpTu0tfOcdbkm_qoL4",
"txId": "mvprJ3C3pLIdqEzHnbMnd3v2g3YNMMDTvCdKEXxewiE",
"winston": "10000000"
}
],
"fees": {
"mvprJ3C3pLIdqEzHnbMnd3v2g3YNMMDTvCdKEXxewiE": "41395224"
}
}
実際にアップロードしてからArDriveの画面上に出てくるまでかなりのタイムラグがありますが、ArDrive上にもしっかりと反映されていました。
フォルダ自体を指定したせいか、フォルダ毎アップロードされていました。
ArDriveの画面上反映されるのは時間がかかりましたが、CLI上だとアップロード完了後から確認は可能です。
$ ardrive list-folder --parent-folder-id "dc349635-c426-4f8e-b03d-f5a208c179fc"
[
{
"appName": "ArDrive-CLI",
"appVersion": "1.22.0",
"arFS": "0.11",
"contentType": "application/json",
"driveId": "1cf5a39f-8999-4c70-a69b-ac3ca8b96eab",
"entityType": "folder",
"name": "metadata",
"txId": "CD13St_e1nRhDgNp7a-07c1k7ETtydEnk5q68Du0csw",
"unixTime": 1669213298,
"parentFolderId": "dc349635-c426-4f8e-b03d-f5a208c179fc",
"entityId": "7f6e81ae-c0da-4728-a047-d09574cf8a02",
"folderId": "7f6e81ae-c0da-4728-a047-d09574cf8a02",
"path": "/sandbox/ArDriveCLITest/metadata",
"txIdPath": "/qJl0XRkg2l6wcYIpmpL69EtjUeuS-4AsVM5_HzT1chc/iF59iRJbh1_S2Xppke1NacmUxIoSG29Zw5SrvlC6jSc/CD13St_e1nRhDgNp7a-07c1k7ETtydEnk5q68Du0csw",
"entityIdPath": "/a8ff1c8f-a733-4afb-a02a-cb670bea29fa/dc349635-c426-4f8e-b03d-f5a208c179fc/7f6e81ae-c0da-4728-a047-d09574cf8a02"
}
]
作成されたフォルダIDの中にもきっちりファイルが格納されています。
$ ardrive list-folder --parent-folder-id "7f6e81ae-c0da-4728-a047-d09574cf8a02"
[
{
"appName": "ArDrive-CLI",
"appVersion": "1.22.0",
"arFS": "0.11",
"contentType": "application/json",
"driveId": "1cf5a39f-8999-4c70-a69b-ac3ca8b96eab",
"entityType": "file",
"name": "0.json",
"txId": "qDUbc4mDByX1fk5IZd4iR2KzwAYB4tjOsJWKTDpW0eg",
"unixTime": 1669213298,
"size": 1440,
"lastModifiedDate": 1668511410832,
"dataTxId": "4FuinPa6xO2OiS7BffZdnOraRl__wxL4mq3Vb6z496g",
"dataContentType": "application/json",
"parentFolderId": "7f6e81ae-c0da-4728-a047-d09574cf8a02",
"entityId": "10f49d1a-877c-43c5-8ee8-e263206d0e2e",
"fileId": "10f49d1a-877c-43c5-8ee8-e263206d0e2e",
"path": "/sandbox/ArDriveCLITest/metadata/0.json",
"txIdPath": "/qJl0XRkg2l6wcYIpmpL69EtjUeuS-4AsVM5_HzT1chc/iF59iRJbh1_S2Xppke1NacmUxIoSG29Zw5SrvlC6jSc/CD13St_e1nRhDgNp7a-07c1k7ETtydEnk5q68Du0csw/qDUbc4mDByX1fk5IZd4iR2KzwAYB4tjOsJWKTDpW0eg",
"entityIdPath": "/a8ff1c8f-a733-4afb-a02a-cb670bea29fa/dc349635-c426-4f8e-b03d-f5a208c179fc/7f6e81ae-c0da-4728-a047-d09574cf8a02/10f49d1a-877c-43c5-8ee8-e263206d0e2e"
},
{
"appName": "ArDrive-CLI",
"appVersion": "1.22.0",
"arFS": "0.11",
"contentType": "application/json",
"driveId": "1cf5a39f-8999-4c70-a69b-ac3ca8b96eab",
"entityType": "file",
"name": "1.json",
"txId": "u8Qp4A_6Bx88APclpLZJ8KMK4IxAaTbI6U9Fe1SzaGU",
"unixTime": 1669213298,
"size": 1440,
"lastModifiedDate": 1668511420831,
"dataTxId": "UZz6Wb710T-M_M0mWbnukc2j_GNFHcw8fBFaSw8re2s",
"dataContentType": "application/json",
"parentFolderId": "7f6e81ae-c0da-4728-a047-d09574cf8a02",
"entityId": "66334dec-5e17-4ef0-9dfd-058694186698",
"fileId": "66334dec-5e17-4ef0-9dfd-058694186698",
"path": "/sandbox/ArDriveCLITest/metadata/1.json",
"txIdPath": "/qJl0XRkg2l6wcYIpmpL69EtjUeuS-4AsVM5_HzT1chc/iF59iRJbh1_S2Xppke1NacmUxIoSG29Zw5SrvlC6jSc/CD13St_e1nRhDgNp7a-07c1k7ETtydEnk5q68Du0csw/u8Qp4A_6Bx88APclpLZJ8KMK4IxAaTbI6U9Fe1SzaGU",
"entityIdPath": "/a8ff1c8f-a733-4afb-a02a-cb670bea29fa/dc349635-c426-4f8e-b03d-f5a208c179fc/7f6e81ae-c0da-4728-a047-d09574cf8a02/66334dec-5e17-4ef0-9dfd-058694186698"
},
{
"appName": "ArDrive-CLI",
"appVersion": "1.22.0",
"arFS": "0.11",
"contentType": "application/json",
"driveId": "1cf5a39f-8999-4c70-a69b-ac3ca8b96eab",
"entityType": "file",
"name": "2.json",
"txId": "ZRELrf74uPWQHtTT9DIVJ0WIgd_3YSkwd1Bpf6wJHTA",
"unixTime": 1669213298,
"size": 1440,
"lastModifiedDate": 1668511428677,
"dataTxId": "CCxyxom6h37PZ7iDBQDd6myzav9-U7r63LuJpefq74E",
"dataContentType": "application/json",
"parentFolderId": "7f6e81ae-c0da-4728-a047-d09574cf8a02",
"entityId": "47f4935f-60db-4a24-a555-3fa142714970",
"fileId": "47f4935f-60db-4a24-a555-3fa142714970",
"path": "/sandbox/ArDriveCLITest/metadata/2.json",
"txIdPath": "/qJl0XRkg2l6wcYIpmpL69EtjUeuS-4AsVM5_HzT1chc/iF59iRJbh1_S2Xppke1NacmUxIoSG29Zw5SrvlC6jSc/CD13St_e1nRhDgNp7a-07c1k7ETtydEnk5q68Du0csw/ZRELrf74uPWQHtTT9DIVJ0WIgd_3YSkwd1Bpf6wJHTA",
"entityIdPath": "/a8ff1c8f-a733-4afb-a02a-cb670bea29fa/dc349635-c426-4f8e-b03d-f5a208c179fc/7f6e81ae-c0da-4728-a047-d09574cf8a02/47f4935f-60db-4a24-a555-3fa142714970"
},
{
"appName": "ArDrive-CLI",
"appVersion": "1.22.0",
"arFS": "0.11",
"contentType": "application/json",
"driveId": "1cf5a39f-8999-4c70-a69b-ac3ca8b96eab",
"entityType": "file",
"name": "3.json",
"txId": "gw4C7vFxaYEGjYAa8LB-gaweQ6_m_Too6TdkWSvcKCU",
"unixTime": 1669213298,
"size": 1440,
"lastModifiedDate": 1668511153646,
"dataTxId": "tAdhTNY4RQNDlMzPTDj6Tdq9w_TYebvrSC61hORDL0E",
"dataContentType": "application/json",
"parentFolderId": "7f6e81ae-c0da-4728-a047-d09574cf8a02",
"entityId": "7d6b3ba9-9b11-41ff-8f93-42365bff1229",
"fileId": "7d6b3ba9-9b11-41ff-8f93-42365bff1229",
"path": "/sandbox/ArDriveCLITest/metadata/3.json",
"txIdPath": "/qJl0XRkg2l6wcYIpmpL69EtjUeuS-4AsVM5_HzT1chc/iF59iRJbh1_S2Xppke1NacmUxIoSG29Zw5SrvlC6jSc/CD13St_e1nRhDgNp7a-07c1k7ETtydEnk5q68Du0csw/gw4C7vFxaYEGjYAa8LB-gaweQ6_m_Too6TdkWSvcKCU",
"entityIdPath": "/a8ff1c8f-a733-4afb-a02a-cb670bea29fa/dc349635-c426-4f8e-b03d-f5a208c179fc/7f6e81ae-c0da-4728-a047-d09574cf8a02/7d6b3ba9-9b11-41ff-8f93-42365bff1229"
},
{
"appName": "ArDrive-CLI",
"appVersion": "1.22.0",
"arFS": "0.11",
"contentType": "application/json",
"driveId": "1cf5a39f-8999-4c70-a69b-ac3ca8b96eab",
"entityType": "file",
"name": "4.json",
"txId": "yp_bq1HsSW-6PFAWAMOm0q5bW8fMNgUgQyOfSsahjNs",
"unixTime": 1669213298,
"size": 1437,
"lastModifiedDate": 1668511175537,
"dataTxId": "FLJ2jixnae-WMqIkZ6zb7I4ScL9L-W3ta66SuFe3OMU",
"dataContentType": "application/json",
"parentFolderId": "7f6e81ae-c0da-4728-a047-d09574cf8a02",
"entityId": "55b8fa68-bcf9-414b-a44b-e59e634b1e9f",
"fileId": "55b8fa68-bcf9-414b-a44b-e59e634b1e9f",
"path": "/sandbox/ArDriveCLITest/metadata/4.json",
"txIdPath": "/qJl0XRkg2l6wcYIpmpL69EtjUeuS-4AsVM5_HzT1chc/iF59iRJbh1_S2Xppke1NacmUxIoSG29Zw5SrvlC6jSc/CD13St_e1nRhDgNp7a-07c1k7ETtydEnk5q68Du0csw/yp_bq1HsSW-6PFAWAMOm0q5bW8fMNgUgQyOfSsahjNs",
"entityIdPath": "/a8ff1c8f-a733-4afb-a02a-cb670bea29fa/dc349635-c426-4f8e-b03d-f5a208c179fc/7f6e81ae-c0da-4728-a047-d09574cf8a02/55b8fa68-bcf9-414b-a44b-e59e634b1e9f"
}
]
4. マニュフェストの作成
最後にフォルダ階層でアクセスできるようにマニュフェストファイルを作成します。
マニュフェストファイルの作成もCLIから問題なく行えます。
これでフォルダ階層をきっちり保った状態で各ファイルへのアクセスが行えます。
$ ardrive create-manifest -f "7f6e81ae-c0da-4728-a047-d09574cf8a02" -w ./wallet.json
{
"created": [
{
"type": "file",
"entityName": "DriveManifest.json",
"entityId": "d1f3a6b2-6047-4eee-9d8d-ed8efade1567",
"dataTxId": "rvyFDuYiTpb6iazNkAouq9lzl_fwUfTgOUU3wSOwkeA",
"metadataTxId": "uueb3bPR1fR7TJo5I64-gA-ZGRDLzcU1O6VM0BHGFog",
"bundledIn": "xSIR7XmISjUlahVoYIk_XSf475oBM57MkCJhjm3jupI"
},
{
"type": "bundle",
"bundleTxId": "xSIR7XmISjUlahVoYIk_XSf475oBM57MkCJhjm3jupI"
}
],
"tips": [
{
"recipient": "Zznp65qgTIm2QBMjjoEaHKOmQrpTu0tfOcdbkm_qoL4",
"txId": "xSIR7XmISjUlahVoYIk_XSf475oBM57MkCJhjm3jupI",
"winston": "10000000"
}
],
"fees": {
"xSIR7XmISjUlahVoYIk_XSf475oBM57MkCJhjm3jupI": "41395224"
},
"manifest": {
"manifest": "arweave/paths",
"version": "0.1.0",
"index": {
"path": "0.json"
},
"paths": {
"0.json": {
"id": "4FuinPa6xO2OiS7BffZdnOraRl__wxL4mq3Vb6z496g"
},
"1.json": {
"id": "UZz6Wb710T-M_M0mWbnukc2j_GNFHcw8fBFaSw8re2s"
},
"2.json": {
"id": "CCxyxom6h37PZ7iDBQDd6myzav9-U7r63LuJpefq74E"
},
"3.json": {
"id": "tAdhTNY4RQNDlMzPTDj6Tdq9w_TYebvrSC61hORDL0E"
},
"4.json": {
"id": "FLJ2jixnae-WMqIkZ6zb7I4ScL9L-W3ta66SuFe3OMU"
}
}
},
"links": [
"https://arweave.net/rvyFDuYiTpb6iazNkAouq9lzl_fwUfTgOUU3wSOwkeA",
"https://arweave.net/rvyFDuYiTpb6iazNkAouq9lzl_fwUfTgOUU3wSOwkeA/0.json",
"https://arweave.net/rvyFDuYiTpb6iazNkAouq9lzl_fwUfTgOUU3wSOwkeA/1.json",
"https://arweave.net/rvyFDuYiTpb6iazNkAouq9lzl_fwUfTgOUU3wSOwkeA/2.json",
"https://arweave.net/rvyFDuYiTpb6iazNkAouq9lzl_fwUfTgOUU3wSOwkeA/3.json",
"https://arweave.net/rvyFDuYiTpb6iazNkAouq9lzl_fwUfTgOUU3wSOwkeA/4.json"
]
}
実際にArDriveCLIで大量ファイルアップロードは注意が必要
少数でテストをしてDryRunも試して良さそうだったので実際に2222枚の画像(1枚あたり500kb程度)で合計1GBもいかないファイルを一括でアップロードを試しました。
処理状況等も何も出ないのでファイルサイズも大きいから時間もかかるかなと気長に待っていたのですが、3時間後に見た時にも何も変わっていなかったので流石におかしいと処理をキャンセルしました。
後からDryRunの結果とArweaveのトランザクションを確認した感じだと今回の一括アップロードは処理が9分割されて9つのトランザクションが発行されるはずでしたが、1つしかトランザクションが発行されず、1/9しかファイルもアップロードされていませんでした。
再アップロードも試そうとしましたが、内部でゴミトランザクションが溜まっているのかコマンド自体がエラーになってしまいどうしようもないことに。
結局最終的にはArDriveの画面から500件ずつファイルを手動でアップロードして対応しました。
CLIならうまくいくと思っていたら完全に引っかかりました。
ArDriveとArDrive CLIを使ってみて
今回ArDriveのWeb GUIとCLIを利用してみての個人的な所感
ArDrive/ArDrive CLIの良いところ
- ArDriveのWebサービス上でGUIでフォルダ一覧やファイル一覧が確認できる
- フォルダの移動やファイルのリネームなど通常のファイルと同じように行える
ArDrive/ArDrive CLIの微妙なところ
- ArDrive CLIで一旦エラーが出ると永遠とエラー
- ArDriveのWebサービスは定期的に同期処理が数十秒〜数分間走り操作ができない
- ArDrive CLIでアップロードしたフォルダやファイルのGUIへの反映に時間がかかる
ArDriveCLI以外のCLIの選択肢
Bundlrなども良さそうには見えます。
CLI以外も豊富でサービスに組み込む際にも使えそうです。
今回ArDriveCLIには痛い目に合わされたので今度はBundlrでも使ってみようかなと思います。
Discussion