Работа с S3 и облачными хранилищами в TypeScript: полный гайд

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с S3 и облачными хранилищами в TypeScript

Современные веб-приложения всё чаще используют облачные объектные хранилища для хранения файлов, изображений, резервных копий и статического контента. Amazon S3 (Simple Storage Service) является де-факто стандартом в этой области, а его API поддерживается большинством облачных провайдеров и локальных решений, таких как MinIO, DigitalOcean Spaces или Yandex Object Storage. В этой статье мы рассмотрим, как работать с S3-совместимыми хранилищами в TypeScript, используя официальный AWS SDK и альтернативные библиотеки.

Выбор библиотеки для работы с S3 в TypeScript

Для взаимодействия с S3 из TypeScript существует несколько популярных решений:

  • @aws-sdk/client-s3 — официальный SDK от Amazon, написанный на TypeScript, модульный и современный.
  • aws-sdk — классическая версия SDK (v2), которая всё ещё поддерживается, но рекомендуется переходить на v3.
  • minio — клиентская библиотека для MinIO и любых S3-совместимых хранилищ, простая и лёгкая.
  • s3-client — минималистичная обёртка над fetch API для работы с S3.

Мы сосредоточимся на @aws-sdk/client-s3 как на наиболее актуальном и мощном инструменте, а также покажем примеры с minio для случаев, когда нужна лёгкая альтернатива.

Установка и настройка

Установите необходимые пакеты через npm:

npm install @aws-sdk/client-s3 @aws-sdk/lib-storage
npm install --save-dev @types/node typescript

Для работы с MinIO:

npm install minio

Теперь создадим базовый клиент S3. Убедитесь, что у вас есть переменные окружения или конфигурация с ключами доступа.

Пример 1: Инициализация клиента S3

import { S3Client } from "@aws-sdk/client-s3";

const s3Client = new S3Client({
  region: process.env.AWS_REGION || "us-east-1",
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
  },
  // Для MinIO или локального S3 укажите endpoint:
  // endpoint: "http://localhost:9000",
  // forcePathStyle: true,
});

export default s3Client;

Обратите внимание на параметр forcePathStyle — он обязателен для MinIO и некоторых других не-AWS хранилищ.

Пример 2: Загрузка файла в S3

Для загрузки файла используем команду PutObjectCommand:

import { PutObjectCommand } from "@aws-sdk/client-s3";
import s3Client from "./s3Client";

async function uploadFile(bucket: string, key: string, body: Buffer | string) {
  const command = new PutObjectCommand({
    Bucket: bucket,
    Key: key,
    Body: body,
    ContentType: "application/octet-stream",
  });

  try {
    const response = await s3Client.send(command);
    console.log("Файл загружен:", response.$metadata.httpStatusCode);
    return response;
  } catch (error) {
    console.error("Ошибка загрузки:", error);
    throw error;
  }
}

// Использование:
await uploadFile("my-bucket", "uploads/image.png", bufferFromFile);

Для потоковой загрузки больших файлов используйте @aws-sdk/lib-storage:

import { Upload } from "@aws-sdk/lib-storage";
import { Readable } from "stream";

const upload = new Upload({
  client: s3Client,
  params: {
    Bucket: "my-bucket",
    Key: "large-file.zip",
    Body: Readable.from(fileStream),
  },
});

upload.on("httpUploadProgress", (progress) => {
  console.log(`Загружено: ${progress.loaded} из ${progress.total}`);
});

await upload.done();

Пример 3: Скачивание файла

Для получения объекта из хранилища используем GetObjectCommand:

import { GetObjectCommand } from "@aws-sdk/client-s3";
import s3Client from "./s3Client";

async function downloadFile(bucket: string, key: string): Promise<Buffer> {
  const command = new GetObjectCommand({
    Bucket: bucket,
    Key: key,
  });

  const response = await s3Client.send(command);
  const stream = response.Body as Readable;

  return new Promise((resolve, reject) => {
    const chunks: Buffer[] = [];
    stream.on("data", (chunk) => chunks.push(chunk));
    stream.on("end", () => resolve(Buffer.concat(chunks)));
    stream.on("error", reject);
  });
}

// Пример использования:
const fileBuffer = await downloadFile("my-bucket", "documents/report.pdf");                    

Рекомендаций