Drive
AdonisJS Drive (@adonisjs/drive
) は flydrive.dev の上に構築された軽量なラッパーライブラリです。FlyDriveはNode.jsのファイルストレージライブラリです。ローカルファイルシステムやS3、R2、GCSなどのクラウドストレージソリューションと統一されたAPIを提供します。
FlyDriveを使用することで、コードを1行も変更することなく、さまざまなクラウドストレージサービス(ローカルファイルシステムを含む)でユーザーがアップロードしたファイルを管理できます。
インストール
以下のコマンドを使用して @adonisjs/drive
パッケージをインストールし、設定します:
node ace add @adonisjs/drive
-
検出されたパッケージマネージャを使用して
@adonisjs/drive
パッケージをインストールします。 -
adonisrc.ts
ファイル内に以下のサービスプロバイダを登録します。{providers: [// ...other providers() => import('@adonisjs/drive/drive_provider'),]} -
config/drive.ts
ファイルを作成します。 -
選択したストレージサービスのための環境変数を定義します。
-
選択したストレージサービスの必要なピア依存関係をインストールします。
設定
@adonisjs/drive
パッケージの設定は config/drive.ts
ファイルに保存されます。1つの設定ファイル内で複数のサービスの設定を定義できます。
参照: Config stub
import env from '#start/env'
import app from '@adonisjs/core/services/app'
import { defineConfig, services } from '@adonisjs/drive'
const driveConfig = defineConfig({
default: env.get('DRIVE_DISK'),
services: {
/**
* ローカルファイルシステムにファイルを保存します
*/
fs: services.fs({
location: app.makePath('storage'),
serveFiles: true,
routeBasePath: '/uploads',
visibility: 'public',
}),
/**
* Digital Ocean Spaces にファイルを保存します
*/
spaces: services.s3({
credentials: {
accessKeyId: env.get('SPACES_KEY'),
secretAccessKey: env.get('SPACES_SECRET'),
},
region: env.get('SPACES_REGION'),
bucket: env.get('SPACES_BUCKET'),
endpoint: env.get('SPACES_ENDPOINT'),
visibility: 'public',
}),
},
})
export default driveConfig
環境変数
ストレージサービスの認証情報や設定は、.env
ファイル内の環境変数として保存されます。Driveを使用する前に値を更新してください。
また、DRIVE_DISK
環境変数はファイルを管理するためのデフォルトのディスク/サービスを定義します。たとえば、開発環境では fs
ディスクを使用し、本番環境では spaces
ディスクを使用できます。
使用法
Driveを設定した後、drive
サービスをインポートしてそのAPIとやり取りできます。以下の例では、Driveを使用してファイルのアップロード操作を処理しています。
AdonisJSの統合はFlyDriveのシンプルなラッパーです。APIをより理解するためには、FlyDrive ドキュメントを読むことをオススメします。
import { cuid } from '@adonisjs/core/helpers'
import drive from '@adonisjs/drive/services/main'
import router from '@adonisjs/core/services/router'
router.put('/me', async ({ request, response }) => {
/**
* ステップ1: リクエストから画像を取得し、基本的なバリデーションを行います
*/
const image = request.file('avatar', {
size: '2mb',
extnames: ['jpeg', 'jpg', 'png'],
})
if (!image) {
return response.badRequest({ error: '画像がありません' })
}
/**
* ステップ2: Drive を使用して一意の名前で画像を移動します
*/
const key = `uploads/${cuid()}.${image.extname}`
await image.moveToDisk(key)
/**
* ファイルの公開URLを返します
*/
return {
message: '画像がアップロードされました',
url: await drive.use().getUrl(key),
}
})
-
DriveパッケージはMultipartFile に
moveToDisk
メソッドを追加します。このメソッドはファイルをtmpPath
から設定されたストレージプロバイダにコピーします。 -
drive.use().getUrl()
メソッドはファイルの公開URLを返します。プライベートファイルの場合は、getSignedUrl
メソッドを使用する必要があります。
Drive サービス
@adonisjs/drive/services/main
パスからエクスポートされるDriveサービスは、config/drive.ts
ファイルからエクスポートされた設定を使用して作成された DriveManager クラスのシングルトンインスタンスです。
このサービスをインポートしてDriveManagerと設定されたファイルストレージサービスとやり取りできます。例:
import drive from '@adonisjs/drive/services/main'
drive instanceof DriveManager // true
/**
* デフォルトディスクのインスタンスを返します
*/
const disk = drive.use()
/**
* 名前付きディスクのインスタンスを返します
*/
const disk = drive.use('r2')
/**
* 名前付きディスクのインスタンスを返します
*/
const disk = drive.use('spaces')
Diskのインスタンスを取得したら、それを使用してファイルを管理できます。
参照: Disk API
await disk.put(key, value)
await disk.putStream(key, readableStream)
await disk.get(key)
await disk.getStream(key)
await disk.getArrayBuffer(key)
await disk.delete(key)
await disk.deleteAll(prefix)
await disk.copy(source, destination)
await disk.move(source, destination)
await disk.copyFromFs(source, destination)
await disk.moveFromFs(source, destination)
ローカルファイルシステムドライバ
AdonisJSの統合はFlyDriveのローカルファイルシステムドライバを拡張し、URLの生成とAdonisJSのHTTPサーバーを使用してファイルを提供する機能を追加します。
以下は、ファイルシステムドライバを設定するために使用できるオプションのリストです。
{
services: {
fs: services.fs({
location: app.makePath('storage'),
visibility: 'public',
appUrl: env.get('APP_URL'),
serveFiles: true,
routeBasePath: '/uploads',
}),
}
}
-
location
-
location
プロパティは、ファイルを保存するストアを定義します。このディレクトリは.gitignore
に追加する必要があります。これにより、開発中にアップロードされたファイルが本番サーバーにプッシュされないようになります。 -
visibility
-
visibility
プロパティは、ファイルを公開または非公開にするために使用されます。非公開ファイルは署名付きURLを使用してのみアクセスできます。詳細を参照してください。 -
serveFiles
-
serveFiles
オプションは、ローカルファイルシステムからファイルを提供するためのルートを自動的に登録します。このルートは list:routes ace コマンドを使用して表示できます。 -
routeBasePath
-
routeBasePath
オプションは、ファイルを提供するためのルートのベースプレフィックスを定義します。ベースプレフィックスが一意であることを確認してください。 -
appUrl
-
appUrl
プロパティをオプションで定義すると、アプリケーションの完全なドメイン名を使用してURLを作成できます。それ以外の場合は相対URLが作成されます。
Edge ヘルパー
Edge テンプレート内では、次のヘルパーメソッドのいずれかを使用してURLを生成できます。どちらのメソッドも非同期ですので、await
を使用してください。
<img src="{{ await driveUrl(user.avatar) }}" />
<!-- 名前付きディスクのためのURLを生成します -->
<img src="{{ await driveUrl(user.avatar, 's3') }}" />
<img src="{{ await driveUrl(user.avatar, 'r2') }}" />
<a href="{{ await driveSignedUrl(invoice.key) }}">
請求書をダウンロード
</a>
<!-- 名前付きディスクのためのURLを生成します -->
<a href="{{ await driveSignedUrl(invoice.key, 's3') }}">
請求書をダウンロード
</a>
<!-- 署名付きオプションを使用してURLを生成します -->
<a href="{{ await driveSignedUrl(invoice.key, {
expiresIn: '30 mins',
}) }}">
請求書をダウンロード
</a>
MultipartFile ヘルパー
DriveはBodyparserの MultipartFile クラスを拡張し、moveToDisk
メソッドを追加します。このメソッドはファイルを tmpPath
から設定されたストレージプロバイダにコピーします。
const image = request.file('image')!
const key = 'user-1-avatar.png'
/**
* デフォルトディスクにファイルを移動します
*/
await image.moveToDisk(key)
/**
* 名前付きディスクにファイルを移動します
*/
await image.moveToDisk(key, 's3')
/**
* 移動操作中に追加のプロパティを定義します
*/
await image.moveToDisk(key, 's3', {
contentType: 'image/png',
})
テスト中のディスクのフェイク
テスト中にリモートストレージとのやり取りを防ぐために、Drive のフェイクAPIを使用できます。フェイクモードでは、drive.use()
メソッドはフェイクディスク(ローカルファイルシステムをバックエンドとする)を返し、すべてのファイルはアプリケーションのルートディレクトリの ./tmp/drive-fakes
ディレクトリに書き込まれます。
これらのファイルは、drive.restore
メソッドを使用してフェイクを復元すると自動的に削除されます。
import { test } from '@japa/runner'
import drive from '@adonisjs/drive/services/main'
import fileGenerator from '@poppinss/file-generator'
test.group('Users | update', () => {
test('自分のアバターを更新できること', async ({ client, cleanup }) => {
/**
* "spaces" ディスクをフェイクし、テストが終了したらフェイクを復元します
*/
const fakeDisk = drive.fake('spaces')
cleanup(() => drive.restore('spaces'))
/**
* ログインと更新を行うためのユーザーを作成します
*/
const user = await UserFactory.create()
/**
* 1MB のサイズのフェイクなメモリ内 PNG ファイルを生成します
*/
const { contents, mime, name } = await fileGenerator.generatePng('1mb')
/**
* ファイルを送信するための put リクエストを作成します
*/
await client
.put('me')
.file('avatar', contents, {
filename: name,
contentType: mime,
})
.loginAs(user)
/**
* ファイルが存在することをアサートします
*/
fakeDisk.assertExists(user.avatar)
})
})