При разработке веб-приложений в первую очередь нужно решить, где будут храниться файлы, с которыми работает приложение. В традиционных веб-приложениях файлы обычно размещаются на сервере, который обслуживает приложение. Однако этот метод имеет свои ограничения: сложно эффективно управлять большим количеством данных и обеспечить их безопасность. Более современным и удобным решением является использование облачного хранилища, которое позволяет легко сохранять и получать файлы через интернет. Одним из вариантов облачного хранилища является объектное хранилище S3.
Объектное хранилище — это тип хранения данных, где информация сохраняется как отдельные объекты, что позволяет эффективно управлять большими объемами данных. В нашем случае объекты — это файлы, которые мы будем загружать и скачивать с облака. В этой статье мы покажем, как настроить S3-хранилище Timeweb Cloud и подключить его к вашему веб-приложению.
Наш проект разрабатывается на .NET 9 — одной из самых последних версий фреймворка от Microsoft. Эта платформа обеспечивает высокую производительность веб-приложений, особенно при использовании ASP.NET, что позволяет эффективно обрабатывать пользовательские запросы через интернет.
В этой статье мы рассмотрим следующие ключевые этапы настройки нашего проекта:
Создание проекта в Timeweb Cloud — создадим проект и объектное хранилище в Timeweb Cloud и настроим его для хранения наших файлов.
Развертывание ASP.NET-проекта — создадим веб-приложение на ASP.NET и подготовим его для работы, настроив все необходимые компоненты для обработки запросов.
Установка необходимых NuGet-пакетов — NuGet — это система для управления библиотеками и пакетами в .NET. Мы подключим библиотеки, которые помогут работать с S3-хранилищем, чтобы мы могли загружать и скачивать файлы.
Добавление поддержки Scalar — подключим поддержку OpenAPI с помощью Scalar, чтобы упростить тестирование нашего API (интерфейса для взаимодействия с приложением).
Настройка переменных окружения — для надежного хранения ключей доступа к S3-хранилищу настроим переменные окружения, чтобы хранить конфиденциальную информацию в безопасном месте, а не прямо в коде.
Создание минимального Docker-контейнера — Docker — это инструмент, который позволяет упаковать приложение в контейнер, что упрощает его развертывание на разных серверах. Мы создадим контейнер, чтобы проще развернуть наше приложение в облаке.
Подключение к S3 API — научимся работать с S3 API, чтобы загружать и скачивать файлы.
Разработка контроллеров — добавим эндпоинты (адреса, по которым приложение будет получать запросы) для работы с хранилищем, чтобы пользователи могли загружать и скачивать файлы через интернет.
Весь процесс будет пошаговым, и в конце вы получите работающий API, который будет взаимодействовать с облачным хранилищем S3, позволяя загружать и скачивать файлы.
s3
Перейдите в панель управления Timeweb Cloud и создайте новый проект.
В панели управления создайте S3-хранилище. Выберите подходящий тариф (для тестового развертывания достаточно минимального конфигурации) и перейдите в бакет — контейнер для хранения объектов.
Сохраните три ключевых значения:
Выполнение этого и следующих шагов также представлено на скринкасте.
Откройте консоль и создайте директорию для проекта:
mkdir timeweb-s3-api
Затем перейдите в эту директорию:
cd timeweb-s3-api
Инициализируйте проект командой:
dotnet new webapi
Проект будет создан с версией .NET 9. Чтобы убедиться в этом, откройте файл с расширением .csproj
и найдите строку:
<TargetFramework>net9.0</TargetFramework>
Для минимальной конфигурации файл Program.cs
должен выглядеть так:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.Run();
Для работы с S3 и нужными библиотеками установите необходимые пакеты, поочередно выполнив следующие команды:
dotnet add package AWSSDK.Extensions.NETCore.Setup --version 4.0.0-preview
dotnet add package AWSSDK.S3 --version 4.0.0-preview
dotnet add package DotNetEnv --version 3.1.1
dotnet add package Microsoft.AspNetCore.OpenApi --version 9.0.2
dotnet add package Scalar.AspNetCore --version 2.0.25
После установки пакетов дождитесь их загрузки и подтвердите установку последнего пакета, снова нажав Enter.
Затем соберите проект, выполнив команду:
dotnet build
Создайте файл GlobalUsings.cs
в корне проекта и добавьте в него следующие пространства имен:
global using Scalar.AspNetCore;
global using DotNetEnv;
global using Amazon.S3;
global using Microsoft.Extensions.Options;
global using Microsoft.AspNetCore.Mvc;
global using Amazon.S3.Model;
global using System.Net;
Этот файл позволит избежать дублирования директив using
в каждом файле проекта, обеспечивая доступ к необходимым библиотекам везде, где это потребуется.
Добавьте поддержку Scalar API, чтобы упростить тестирование приложения.
Для этого откройте Program.cs
и сразу после строки var app = builder.Build();
добавьте:
app.MapScalarApiReference();
Это создаст страницу http://localhost/scalar
, где веб-клиент сможет видеть доступные API-эндпоинты — адреса, по которым можно отправлять запросы.
Создайте файл .env
в корневой папке проекта. В этом файле будут храниться переменные окружения — настройки, которые приложение будет использовать при работе.
Замените значения в скобках на свои данные для подключения к бакету.
Пример содержимого файла:
AWS_BUCKET_NAME=[Название бакета]
AWS_ACCESS_KEY=[S3 Access Key]
AWS_SECRET_ACCESS_KEY=[S3 Secret Access Key]
AWS_SERVICE_URL=https://s3.timeweb.cloud
ASPNETCORE_URLS=http://+:80
В итоге файл будет выглядеть подобным образом:
Добавьте в проект переменные окружения. Для этого откройте Program.cs
и вставьте следующий код сразу после строки var builder = WebApplication.CreateBuilder(args)
:
Env.Load();
builder.Configuration.AddEnvironmentVariables();
Создайте Dockerfile в корневой папке проекта. Этот файл нужен, чтобы Docker мог собрать образ — упакованную версию приложения со всеми зависимостями, готовую для запуска в любой среде.
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /app
EXPOSE 80
COPY . .
RUN dotnet restore timeweb-s3-api.csproj
RUN dotnet publish timeweb-s3-api.csproj -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "timeweb-s3-api.dll"]
Добавьте файл docker-compose.yml
со следующим содержимым:
services:
backend:
container_name: backend
build:
context: .
dockerfile: Dockerfile
env_file:
- .env
ports:
- 80:80
networks:
- app-network
networks:
app-network:
driver: bridge
Создайте файл S3Options.cs
в корневой папке проекта. В нем будут храниться настройки для подключения к S3-хранилищу. Добавьте в него следующий код:
public class S3Options
{
public string AccessKey { get; set; } = null!;
public string SecretKey { get; set; } = null!;
public string ServiceUrl { get; set; } = null!;
public string BucketName { get; set; } = null!;
}
Далее для того, чтобы использовать настройки окружения в нашем приложении, в файл Program.cs
после строки builder.Services.AddOpenApi();
добавьте код:
builder.Services.Configure<S3Options>(options =>
{
options.AccessKey = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY") ?? "";
options.SecretKey = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY") ?? "";
options.ServiceUrl = Environment.GetEnvironmentVariable("AWS_SERVICE_URL") ?? "";
options.BucketName = Environment.GetEnvironmentVariable("AWS_BUCKET_NAME") ?? "";
});
builder.Services.AddSingleton<IAmazonS3>(sp =>
{
var options = sp.GetRequiredService<IOptions<S3Options>>().Value;
return new AmazonS3Client(options.AccessKey, options.SecretKey, new AmazonS3Config
{
ServiceURL = options.ServiceUrl,
ForcePathStyle = true
});
});
Он нужен, чтобы передать настройки для работы с S3-хранилищем (например, ключи доступа и адрес сервиса) и сделать их доступными в приложении. Это называется «внедрение зависимостей» — способ передавать нужные параметры в разные части кода без их явного указания в каждом месте.
Для работы с объектным хранилищем S3 от Timeweb Cloud в проекте необходимо создать контроллер S3Controller
. Он будет обрабатывать загрузку, получение и удаление изображений в S3.
В корневой директории создаем папку Controllers
, а в ней — файл S3Controller.cs
. Добавьте в него следующий код:
[ApiController]
[Route("api/s3")]
public class S3Controller : ControllerBase
{
private readonly S3Options _s3Options;
private readonly IAmazonS3 _s3;
public S3Controller(IOptions<S3Options> options, IAmazonS3 s3)
{
_s3Options = options.Value;
_s3 = s3;
}
[HttpPost("image/{id}")]
public async Task<ActionResult<PutObjectResponse>> Upload([FromRoute] int id, [FromForm(Name = "Data")] IFormFile file)
{
var request = new PutObjectRequest
{
BucketName = _s3Options.BucketName,
Key = $"{id}",
ContentType = file.ContentType,
InputStream = file.OpenReadStream(),
};
var response = await _s3.PutObjectAsync(request);
return response.HttpStatusCode == HttpStatusCode.OK ? Ok(response) : BadRequest();
}
[HttpGet("image/{id}")]
public async Task<IActionResult> GetImage([FromRoute] int id)
{
var objectRequest = new GetObjectRequest
{
BucketName = _s3Options.BucketName,
Key = $"{id}"
};
var response = await _s3.GetObjectAsync(objectRequest);
if (response.HttpStatusCode == HttpStatusCode.OK)
return File(response.ResponseStream, response.Headers["Content-Type"]);
return BadRequest();
}
[HttpDelete("image/{id}")]
public async Task<IActionResult> DeleteImage([FromRoute] int id)
{
var objectRequest = new DeleteObjectRequest
{
BucketName = _s3Options.BucketName,
Key = $"{id}"
};
var response = await _s3.DeleteObjectAsync(objectRequest);
return response.HttpStatusCode == HttpStatusCode.NoContent ? NoContent() : BadRequest();
}
}
Контроллер помечается атрибутами:
[ApiController]
— указывает, что этот класс является контроллером API.
[Route("api/s3")]
— задает маршрут для запросов, начинающихся с api/s3
.
Также контроллер использует зависимости:
S3Options
— объект конфигурации с настройками доступа к S3.
IAmazonS3
— клиент для взаимодействия с S3.
Они передаются через конструктор с внедрением зависимостей (IOptions<S3Options>
для конфигурации и IAmazonS3
для работы с S3).
Разберем методы работы с файлами:
Метод [HttpPost("image/{id}")] используется для загрузки изображения в S3.
При вызове метода создается запрос PutObjectRequest
, в котором указываются:
id
— идентификатор файла (например, ID объекта в базе данных).IFormFile file
— полученный файл из multipart/form-data
.Входные параметры метода:
BucketName
— название бакета,Key
— имя файла в S3 (в данном случае переданный id),ContentType
— тип файла,InputStream
— поток загружаемого файла.Запрос передается в PutObjectAsync
, который выполняет загрузку. Если ответ OK (200), загрузка прошла успешно, иначе возвращается ошибка.
Метод [HttpGet("image/{id}")] позволяет получить изображение из S3.
При вызове метода создается запрос GetObjectRequest
, в котором указываются:
BucketName
— название бакета,Key
— идентификатор файла.Входной параметр метода:
id
— идентификатор файла в S3.Метод [HttpDelete("image/{id}")] удаляет изображение из S3.
При вызове метода создается запрос DeleteObjectRequest
, содержащий:
BucketName
— название бакета,Key
— идентификатор файла.Входной параметр метода:
id
— идентификатор файла в S3.После этого выполняется DeleteObjectAsync
. Если статус ответа NoContent (204), файл успешно удален, иначе возвращается ошибка.
Чтобы контроллер начал работать, необходимо добавить в файле Program.cs
после app.MapOpenApi();
следующие строки:
app.MapControllers();
app.UseRouting();
Этот метод регистрирует маршруты для всех контроллеров в приложении, включая S3Controller
.
Также перед строкой var app = builder.Build();
добавьте:
builder.Services.AddControllers();
Вызов функции сообщает ASP.NET Core, что в приложении будут использоваться контроллеры и их нужно зарегистрировать. Без этого контроллеры не будут работать, даже если маршруты настроены.
Для тестирования нашего API, которое теперь доступно по адресу http://localhost
, мы будем использовать два инструмента: Postman и Scalar. В обоих случаях мы будем отправлять HTTP-запросы на следующие эндпоинты:
Эти запросы будут выполняться по шаблону http://localhost/api/s3/image/{id}
, где {id}
— это уникальный идентификатор изображения, с которым мы будем взаимодействовать.
Сначала необходимо запустить контейнер, чтобы приложение стало доступно. Для этого в терминале, находясь в директории проекта, выполните команду:
docker-compose up --build -d
Это создаст и запустит Docker-контейнер, собрав все необходимые компоненты приложения.
Postman — это инструмент для тестирования API. Мы будем использовать его для отправки запросов на наш сервер.
Откройте Postman и введите URL запроса в формате http://localhost/api/s3/image/{id}
, где {id}
— это значение, которое вы хотите использовать для тестирования (например, 1).
Убедитесь, что запрос использует правильный HTTP-метод (GET, POST или DELETE).
Пример GET-запроса
URL: http://localhost/api/s3/image/1
Этот запрос будет пытаться получить файл с ID 1. Ответ будет содержать файл (если он существует) или сообщение об ошибке.
Пример POST-запроса (Загрузка файла)
URL: http://localhost/api/s3/image/1
В теле запроса:
form-data
.Data
— сюда нужно добавить файл для загрузки.Это запрос для загрузки изображения с ID 1 в хранилище S3. В form-data
вы должны прикрепить файл, который хотите загрузить.
Пример DELETE-запроса (Удаление файла)
URL: http://localhost/api/s3/image/1
Этот запрос удаляет файл с ID 1 из S3-хранилища.
Пример из Postman продемонстрирован на картинке ниже.
Scalar — это инструмент для тестирования API с использованием OpenAPI, который подключен для удобства. Он позволяет увидеть список всех доступных эндпоинтов и взаимодействовать с ними прямо из браузера.
При использовании Scalar выполните следующие действия.
http://localhost/scalar
Здесь отобразится список всех доступных эндпоинтов, включая /api/s3/image/{id}
для работы с изображениями.
Примеры запросов в Scalar:
GET-запрос
URL: http://localhost/api/s3/image/1
Этот запрос пытается получить файл с ID 1. Если файл существует, Scalar вернет его с оригинальным Content-Type, иначе — сообщение об ошибке.
POST-запрос (Загрузка файла)
URL: http://localhost/api/s3/image/1
В теле запроса:
form-data
Data
— здесь нужно прикрепить файл для загрузкиЭтот запрос загружает изображение с ID 1 в хранилище S3.
DELETE-запрос
URL: http://localhost/api/s3/image/1
Этот запрос удаляет файл с ID 1 из S3-хранилища.
Пример из Scalar продемонстрирован на картинке ниже.
Тестирование через Postman и Scalar поможет убедиться, что все эндпоинты работают правильно и файлы могут быть загружены, получены и удалены с S3-хранилища.
Надежное облако для ваших проектов
Мы успешно разработали и настроили приложение для работы с файлами в объектном хранилище S3 от Timeweb Cloud: создали хранилище, настроили проект на .NET 9, подключили необходимые библиотеки для работы с S3 и добавили поддержку Scalar. Мы также настроили переменные окружения и создали Docker-контейнер.
Теперь наше приложение позволяет загружать, получать и удалять файлы через API, взаимодействуя с объектным хранилищем S3, что делает его удобным и безопасным для использования в реальных проектах.