Remix — это новый фреймворк на основе реакта, разработанный для приложений полного стека. Во многих приложениях вам понадобится возможность хранить файлы, иногда в сервисе, совместимом с S3. Вот как я смог этого добиться. Большое влияние на меня оказала эта статья на dev.to.
Создайте файл с именем uploader-handler.server.ts
со следующим содержимым:
import { s3Client } from './s3.server';
import type { UploadHandler } from '@remix-run/node';
import type { PutObjectCommandInput } from '@aws-sdk/client-s3';
import { GetObjectCommand } from '@aws-sdk/client-s3';
import { PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const uploadStreamToS3 = async (data: AsyncIterable<Uint8Array>, key: string, contentType: string) => {
const BUCKET_NAME = "my_bucket_name";
const params: PutObjectCommandInput = {
Bucket: BUCKET_NAME,
Key: key,
Body: await convertToBuffer(data),
ContentType: contentType,
};
await s3Client.send(new PutObjectCommand(params));
let url = await getSignedUrl(s3Client, new GetObjectCommand({
Bucket: BUCKET_NAME,
Key: key,
}), { expiresIn: 15 * 60 });
console.log(url);
return key;
}
// The UploadHandler gives us an AsyncIterable<Uint8Array>, so we need to convert that to something the aws-sdk can use.
// Here, we are going to convert that to a buffer to be consumed by the aws-sdk.
async function convertToBuffer(a: AsyncIterable<Uint8Array>) {
const result = [];
for await (const chunk of a) {
result.push(chunk);
}
return Buffer.concat(result);
}
export const s3UploaderHandler: UploadHandler = async ({ filename, data, contentType }) => {
return await uploadStreamToS3(data, filename!, contentType);
}
Далее вам нужно будет создать фактический маршрут, чтобы иметь возможность загрузить файл. У меня есть следующий файл: ~/routes/api/storage/upload.tsx
со следующим содержимым
import type { ActionFunction } from "@remix-run/node";
import { unstable_parseMultipartFormData } from "@remix-run/node";
import { auth } from "~/server/auth.server";
import { s3UploaderHandler } from "~/server/uploader-handler.server";
export const action: ActionFunction = async ({ request }) => {
await auth.isAuthenticated(request, { failureRedirect: '/login' });
const formData = await unstable_parseMultipartFormData(request, s3UploaderHandler);
const fileName = formData.get('upload');
return {
filename: fileName,
}
}
Теперь, когда у вас есть вспомогательные файлы, давайте загрузим файл.
<Form method="post" action={'/api/storage/upload'} encType="multipart/form-data">
<Input type="file" name="upload" />
<Button type="submit">Upload</Button>
</Form>
Вот и все!
Используемая версия sdks:
- @remix-run/node: 1.6.5
- @remix-run/react: 1.6.5
- @aws-sdk/client-s3: 3.145.0
- @aws-sdk/s3-request-presigner: 3.145.0