Поделиться через


Учебное руководство: Загрузка изображения в блоб службы хранилища Azure с помощью TypeScript

В этом руководстве показано, как отправлять файлы из браузера непосредственно в хранилище BLOB-объектов Azure без предоставления учетных данных. Вы будете использовать TypeScript для реализации шаблона «Valet Key» с маркерами SAS и управляемой идентификацией для безопасной аутентификации без использования ключей.

Пример приложения включает:

  • Fastify API, создающее SAS токены с ограниченным временем действия
  • Интерфейс React, который отправляет файлы непосредственно в службу хранилища Azure
  • Инфраструктура в качестве кода для развертывания с помощью Интерфейса командной строки разработчика Azure

В конце этого руководства вы развернете рабочее приложение, развернутое в приложениях контейнеров Azure, которое демонстрирует безопасные отправки файлов без предоставления учетных данных хранилища в браузере.

Необходимые условия

Прежде чем начать, убедитесь, что у вас есть следующее:

Совет

В этом руководстве используются пространства Кода GitHub, которые предоставляют предварительно настроенную среду разработки в браузере. Локальная настройка не требуется.

Architecture

Схема архитектуры Azure, показывающая поток отправки: пользователь выбирает файл в интерфейсном веб-приложении, интерфейсное приложение запрашивает маркер SAS из серверной части API-приложения; серверная часть получает ключ делегирования пользователя из управляемого удостоверения и создаёт маркер SAS для контейнера BLOB-объектов хранилища; интерфейсное приложение отправляет файл непосредственно в хранилище с помощью маркера SAS; серверная часть запрашивает хранилище для перечисления отправленных файлов. Реестр контейнеров предоставляет образы контейнеров для обеих программ.

Интерфейс запрашивает маркер SAS из API, а затем отправляет файлы непосредственно в службу хранилища Azure. После отправки API выводит список всех отправленных файлов с маркерами SAS только для чтения для отображения.

Снимок экрана: веб-приложение с именем

Основные понятия

Токены SAS делегирования пользователей

Приложение использует маркеры SAS с делегированием пользователей для безопасной аутентификации без использования ключа. Эти токены подписываются учетными данными Microsoft Entra ID через Управляемую идентичность. API создает короткие маркеры (10–60 минут) с определенными разрешениями (чтение, запись или удаление), что позволяет браузеру отправлять файлы непосредственно в хранилище без предоставления учетных данных.

Развертывание интерфейса командной строки разработчика Azure

Разверните полную инфраструктуру с помощью azd up. Это подготавливает приложения контейнеров Azure для интерфейсной части и серверной части API Fastify React, настраивает управляемые удостоверения и назначает разрешения RBAC. Инфраструктура использует шаблоны Bicep в соответствии с принципами Azure Well-Architected Framework с проверенными модулями Azure, где они применимы.

Среда контейнера разработки

Полный пример кода в этом руководстве использует контейнер разработки в GitHub Codespaces или локальной версии Visual Studio Code.

Замечание

Вы также можете запустить это руководство локально в Visual Studio Code с расширением "Контейнеры разработки". Полный пример кода включает конфигурацию контейнера разработки.

Открытие примера в GitHub Codespaces

GitHub Codespaces предоставляет среду VS Code на основе браузера со всеми предустановленными зависимостями.

Важный

Все учетные записи GitHub могут использовать пространства Codespaces с бесплатными часами каждый месяц. Дополнительные сведения см. в разделе GitHub Codespaces, ежемесячное включенное хранилище и основные часы.

  1. В веб-браузере откройте образец репозитория и выберите Код>Создать пространственное окружение на главной ветке.

    Снимок экрана: страница репозитория GitHub с выделенной кнопкой

  2. Дождитесь запуска контейнера разработки. Этот процесс запуска может занять несколько минут. Остальные действия, описанные в этом руководстве, происходят в контексте этого контейнера разработки.

Развертывание примера

  1. Войдите в Azure.

    azd auth login
    
  2. Подготовьте ресурсы и разверните образец в хостинговой среде.

    azd up
    

    При появлении запроса введите следующие сведения:

    Подсказка Входите
    Введите уникальное имя среды secure-upload
    Выбор подписки Azure для использования Выберите подписку из списка
    Введите значение параметра инфраструктуры 'location' Выберите из доступных расположений

    Или, если вы хотите просмотреть подготовленные ресурсы, а затем увидеть выходные данные развертывания, вы можете выполнить следующую команду для развертывания без запросов:

    azd provision
    

    Затем выполните следующую команду, чтобы развернуть код приложения:

    azd deploy
    

    При изменении кода API или веб-приложения можно повторно развернуть только код приложения с помощью одной из следующих команд:

    azd deploy app
    azd deploy api
    
  3. По завершении развертывания обратите внимание на URL-адрес развернутого веб-приложения, отображаемого в терминале.

      (✓) Done: Deploying service app
      - Endpoint: https://app-gp2pofajnjhy6.calmtree-87e53015.eastus2.azurecontainerapps.io/
    

    Это пример URL-адреса. Ваш URL-адрес будет отличаться.

Попробуйте пример

  1. Откройте развернутое веб-приложение на новой вкладке браузера и выберите PNG-файл для отправки. В папке ./docs/media доступны несколько PNG-файлов.

    Снимок экрана: веб-приложение для отправки файлов в службу хранилища Azure с кнопкой

  2. Выберите "Получить маркер SAS", а затем нажмите кнопку "Отправить файл".

  3. Просмотрите отправленный файл в коллекции под кнопкой отправки.

    Снимок экрана: веб-приложение после отправки daisies.jpg в службу хранилища Azure, показывающее имя файла, URL-адрес SAS, состояние отправки и эскиз изображения.

Что сейчас произошло?

  • Ваш файл загружен напрямую из браузера в хранилище Azure, используя маркер SAS, действующий в течение ограниченного времени и предназначенный только для записи.
  • Изображения галереи загружаются непосредственно из Azure Storage с помощью SAS-токенов с правами только на чтение
  • В вашем браузере не были скомпрометированы секреты аутентификации.

Как работает код

Теперь, когда вы видели приложение в действии, изучите, как код реализует безопасные отправки файлов. Приложение состоит из двух основных частей:

  1. Серверная часть API — выполняет проверку подлинности с помощью Azure и создает маркеры SAS
  2. Интерфейс React . Отправка файлов непосредственно в службу хранилища Azure с помощью маркеров SAS

В следующих разделах рассматриваются основные реализации кода.

Сервер API для создания маркеров SAS и файлов списка

Сервер API аутентифицируется в службе хранилища Azure и создает временные токены SAS для использования браузера.

Проверка подлинности с помощью управляемого удостоверения

Приложение использует ключи делегирования пользователей с управляемым удостоверением для проверки подлинности, что является наиболее безопасным подходом для приложений Azure. Методы аутентификации ChainedTokenCredential применяются в следующем порядке:

  1. В Azure: ManagedIdentityCredential (идентификация приложений контейнеров)
  2. Локальная разработка: AzureCliCredential (ваш az login сеанс)
// From: packages/api/src/lib/azure-storage.ts
export function getCredential(): ChainedTokenCredential {
  if (!_credential) {
    const clientId = process.env.AZURE_CLIENT_ID;
    
    // Create credential chain with ManagedIdentity first
    const credentials = [
      new ManagedIdentityCredential(clientId ? { clientId } : undefined),
      new AzureCliCredential()
    ];
    
    _credential = new ChainedTokenCredential(...credentials);
  }
  return _credential;
}

После проверки подлинности создайте BlobServiceClient для работы с хранилищем Azure.

// From: packages/api/src/lib/azure-storage.ts
export function getBlobServiceClient(accountName: string): BlobServiceClient {
  const credential = getCredential();
  const url = `https://${accountName}.blob.core.windows.net`;
  
  return new BlobServiceClient(url, credential);
}

Создание маркеров SAS с помощью ключей делегирования пользователей

Для маркеров SAS требуется ключ делегирования пользователей, который проверяет подлинность маркера с помощью учетных данных идентификатора Microsoft Entra, а не ключей учетной записи хранения. Ключ действителен для определенного диапазона времени:

const startsOn = new Date();
const expiresOn = new Date(startsOn.valueOf() + minutes * 60 * 1000);

const userDelegationKey = await blobServiceClient.getUserDelegationKey(
  startsOn,
  expiresOn
);

Создать маркеры SAS только для записи для отправки файлов

Для отправки файлов API создает маркеры, доступные только для записи, которые не могут считывать или удалять данные. Срок действия маркеров истекает через 10 минут:

// From: packages/api/src/routes/sas.ts
const DEFAULT_SAS_TOKEN_PERMISSION = 'w';
const DEFAULT_SAS_TOKEN_EXPIRATION_MINUTES = 10;

const sasToken = generateBlobSASQueryParameters(
  {
    containerName: container,
    blobName: file,
    permissions: BlobSASPermissions.parse(permission),
    startsOn,
    expiresOn
  },
  userDelegationKey,
  accountName
).toString();

const sasUrl = `${blobClient.url}?${sasToken}`;

Доступные уровни разрешений:

  • 'r' — Чтение данных (скачивание и просмотр)
  • 'w' — запись (отправка и перезапись) — используется для отправки
  • 'd' -Удалить
  • 'c' -Создать
  • 'a' — добавление (добавление BLOB-объектов)

Создание маркеров SAS только для чтения для перечисления и просмотра файлов

Для перечисления и отображения файлов API создает маркеры только для чтения, срок действия которого истекает через 60 минут:

// From: packages/api/src/routes/list.ts
const LIST_SAS_TOKEN_PERMISSION = 'r';
const LIST_SAS_TOKEN_EXPIRATION_MINUTES = 60;

const sasToken = generateBlobSASQueryParameters(
  {
    containerName: container,
    blobName: blob.name,
    permissions: BlobSASPermissions.parse(LIST_SAS_TOKEN_PERMISSION),
    startsOn,
    expiresOn
  },
  userDelegationKey,
  accountName
).toString();

const sasUrl = `${blobClient.url}?${sasToken}`;

Запрос клиента веб-приложения и получение маркеров SAS с сервера API

Интерфейс React запрашивает маркеры SAS из API и использует их для непосредственной отправки файлов в службу хранилища Azure из браузера.

Фронтенд следует трехэтапному процессу:

  1. Запрос маркера SAS из API для конкретного файла
  2. Отправка непосредственно в службу хранилища Azure с помощью URL-адреса маркера SAS
  3. Получение и отображение списка отправленных файлов с маркерами SAS только для чтения

Эта архитектура сохраняет упрощенную серверную часть — она создает только токены, никогда не обрабатывает данные файлов.

Запросите SAS-токен для BLOB-хранилища с сервера API

Когда пользователь выбирает файл и нажимает кнопку "Получить маркер SAS", интерфейс запрашивает маркер SAS только для записи из API:

// From: packages/app/src/App.tsx
const handleFileSasToken = () => {
  const permission = 'w'; // write-only
  const timerange = 10;   // 10 minutes expiration

  if (!selectedFile) return;

  // Build API request URL
  const url = `${API_URL}/api/sas?file=${encodeURIComponent(
    selectedFile.name
  )}&permission=${permission}&container=${containerName}&timerange=${timerange}`;

  fetch(url, {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json'
    }
  })
    .then((response) => {
      if (!response.ok) {
        throw new Error(`Error: ${response.status} ${response.statusText}`);
      }
      return response.json();
    })
    .then((data: SasResponse) => {
      const { url } = data;
      setSasTokenUrl(url); // Store the SAS URL for upload
    });
};

Что происходит:

  • Фронтенд отправляет: GET /api/sas?file=photo.jpg&permission=w&container=upload&timerange=10
  • API возвращает: { url: "https://storageaccount.blob.core.windows.net/upload/photo.jpg?sv=2024-05-04&..." }
  • Этот URL-адрес действителен в течение 10 минут и предоставляет доступ только для записи к конкретному блобу.

Загрузка напрямую в хранилище объектов Blob с использованием токена SAS

После получения URL-адреса маркера SAS интерфейс преобразует файл в ArrayBuffer и отправляет файл непосредственно в службу хранилища Azure , обходя API полностью. Это снижает нагрузку сервера и повышает производительность.

Преобразуйте файл в ArrayBuffer.

// From: packages/app/src/lib/convert-file-to-arraybuffer.ts
export function convertFileToArrayBuffer(file: File): Promise<ArrayBuffer | null> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();

    reader.onload = () => {
      const arrayBuffer = reader.result;
      resolve(arrayBuffer as ArrayBuffer);
    };

    reader.onerror = () => {
      reject(new Error('Error reading file.'));
    };

    reader.readAsArrayBuffer(file);
  });
}

Затем используйте BlockBlobClient из @azure/storage-blob, чтобы загрузить данные файла, используя URL-адрес маркера SAS.

// From: packages/app/src/App.tsx
const handleFileUpload = () => {
  console.log('SAS Token URL:', sasTokenUrl);

  // Convert file to ArrayBuffer
  convertFileToArrayBuffer(selectedFile as File)
    .then((fileArrayBuffer) => {
      if (fileArrayBuffer === null || fileArrayBuffer.byteLength < 1) {
        throw new Error('Failed to convert file to ArrayBuffer');
      }

      // Create Azure Storage client with SAS URL
      const blockBlobClient = new BlockBlobClient(sasTokenUrl);
      
      // Upload directly to Azure Storage
      return blockBlobClient.uploadData(fileArrayBuffer);
    })
    .then((uploadResponse) => {
      if (!uploadResponse) {
        throw new Error('Upload failed - no response from Azure Storage');
      }
      setUploadStatus('Successfully finished upload');
      
      // After upload, fetch the updated list of files
      const listUrl = `${API_URL}/api/list?container=${containerName}`;
      return fetch(listUrl);
    });
};

Основные моменты:

  • Файл никогда не передается через сервер API
  • Отправка передается непосредственно из браузера в службу хранилища Azure
  • Маркер SAS проверяет подлинность запроса
  • Нет затрат на пропускную способность сервера или обработку файлов

Извлеките файл непосредственно из хранилища Azure и отобразите миниатюру изображения

После успешной отправки интерфейс извлекает список всех файлов в контейнере. Каждый файл в списке поставляется с собственным маркером SAS только для чтения:

// From: packages/app/src/App.tsx
const listUrl = `${API_URL}/api/list?container=${containerName}`;

fetch(listUrl)
  .then((response) => {
    if (!response.ok) {
      throw new Error(`Error: ${response.status}`);
    }
    return response.json();
  })
  .then((data: ListResponse) => {
    setList(data.list); // Array of SAS URLs with read permission
  });

Пример ответа:

{
  "list": [
    "https://storageaccount.blob.core.windows.net/upload/photo1.jpg?sv=2024-05-04&se=2025-12-18T15:30:00Z&sr=b&sp=r&...",
    "https://storageaccount.blob.core.windows.net/upload/photo2.jpg?sv=2024-05-04&se=2025-12-18T15:30:00Z&sr=b&sp=r&..."
  ]
}

Интерфейс использует URL-адреса SAS непосредственно в тегах изображений. Браузер извлекает изображения из службы хранения Azure с помощью встроенных токенов только для чтения.

// From: packages/app/src/App.tsx
<Grid container spacing={2}>
  {list.map((item) => {
    const urlWithoutQuery = item.split('?')[0];
    const filename = urlWithoutQuery.split('/').pop() || '';
    const isImage = filename.endsWith('.jpg') || 
                    filename.endsWith('.png') || 
                    filename.endsWith('.jpeg');
    
    return (
      <Grid item xs={6} sm={4} md={3} key={item}>
        <Card>
          {isImage ? (
            <CardMedia component="img" image={item} alt={filename} />
          ) : (
            <Typography>{filename}</Typography>
          )}
        </Card>
      </Grid>
    );
  })}
</Grid>

Принцип работы.

  • Каждый URL-адрес в списке включает маркер SAS только для чтения (sp=r)
  • Браузер выполняет запросы GET непосредственно в службу хранилища Azure
  • Проверка подлинности не требуется. Маркер находится в URL-адресе
  • Срок действия маркеров истекает через 60 минут (настроен в API)

Очистка ресурсов

По завершении работы с этим руководством удалите все ресурсы Azure, чтобы избежать текущих расходов:

azd down

Устранение неполадок

Сообщить о проблемах с этим примером в репозитории GitHub. Включите следующее вместе с проблемой:

  • URL-адрес статьи
  • Шаг или контекст статьи, который был проблематичным
  • Среда разработки

Пример кода

Дальнейшие шаги

Теперь, когда вы узнали, как безопасно отправлять файлы в службу хранилища Azure, изучите следующие связанные разделы: