Bagikan melalui


Tutorial: Mengunggah gambar ke blob Azure Storage dengan TypeScript

Tutorial ini menunjukkan kepada Anda cara mengunggah file dari browser langsung ke Azure Blob Storage tanpa mengekspos kredensial. Anda akan menggunakan TypeScript untuk menerapkan pola Kunci Valet dengan token Tanda Tangan Akses Bersama (SAS) dan Identitas Terkelola untuk autentikasi tanpa kunci yang aman.

Aplikasi sampel meliputi:

  • Fastify API yang menghasilkan token SAS berbatas waktu
  • Frontend React yang mengunggah file langsung ke Azure Storage
  • Infrastruktur sebagai kode untuk penerapan dengan Azure Developer CLI

Pada akhir tutorial ini, Anda akan memiliki aplikasi kerja yang disebarkan ke Azure Container Apps yang menunjukkan unggahan file aman tanpa mengekspos kredensial penyimpanan ke browser.

Prasyarat

Sebelum memulai, pastikan Anda memiliki:

Tips

Tutorial ini menggunakan GitHub Codespaces, yang menyediakan lingkungan pengembangan yang telah dikonfigurasi sebelumnya di browser Anda. Tidak diperlukan penyiapan lokal.

Architecture

Diagram arsitektur Azure memperlihatkan alur pengunggahan: Pengguna memilih file di Web App Frontend, frontend meminta token SAS dari API App Backend, backend mendapatkan kunci delegasi pengguna dari Identitas Terkelola dan menghasilkan token SAS dari Kontainer Blob Penyimpanan, mengunggah file frontend langsung ke Penyimpanan menggunakan token SAS, kueri backend Storage untuk mencantumkan file yang diunggah. Container Registry menyediakan gambar kontainer untuk kedua aplikasi.

Frontend meminta token SAS dari API, lalu mengunggah file langsung ke Azure Storage. Setelah diunggah, API mencantumkan semua file yang diunggah dengan token SAS yang hanya bisa dibaca untuk ditampilkan.

Cuplikan layar aplikasi web berjudul 'Unggah file ke Azure Storage' dengan tombol Pilih File dan unggahan nama kontainer ditampilkan.

Konsep utama

Token SAS untuk Delegasi Pengguna

Aplikasi ini menggunakan token SAS Delegasi Pengguna untuk autentikasi tanpa kunci yang aman. Token ini ditandatangani dengan kredensial ID Microsoft Entra melalui Identitas Terkelola. API menghasilkan token berumur pendek (10-60 menit) dengan izin tertentu (baca, tulis, atau hapus), memungkinkan browser mengunggah file langsung ke penyimpanan tanpa mengekspos info masuk.

Penyebaran Azure Developer CLI

Sebarkan infrastruktur lengkap dengan azd up. Ini menyediakan Azure Container Apps untuk backend React dan Fastify API, mengonfigurasi identitas terkelola, dan menetapkan izin RBAC. Infrastruktur ini menggunakan templat Bicep yang mengikuti prinsip-prinsip Azure Well-Architected Framework dengan Modul Azure yang Terverifikasi jika berlaku.

Lingkungan pengembangan kontainer

Kode sampel lengkap tutorial ini menggunakan kontainer pengembangan di GitHub Codespaces atau Visual Studio Code lokal.

Nota

Anda juga dapat menjalankan tutorial ini secara lokal di Visual Studio Code dengan ekstensi Kontainer Dev. Kode sampel lengkap mencakup konfigurasi kontainer pengembangan.

Buka sampel di GitHub Codespaces

GitHub Codespaces menyediakan lingkungan VS Code berbasis browser dengan semua dependensi sudah terinstal.

Penting

Semua akun GitHub dapat menggunakan Codespace dengan jam gratis setiap bulan. Untuk informasi selengkapnya, lihat GitHub Codespaces bulanan yang disertakan penyimpanan dan jam inti.

  1. Di browser web, buka repositori sampel dan pilih Kode>Buat ruang kode di utama.

    Cuplikan layar halaman repositori GitHub memperlihatkan tombol Buka file, Tambahkan file, dan Kode hijau disorot.

  2. Tunggu hingga kontainer pengembangan dimulai. Proses startup ini dapat memakan waktu beberapa menit. Langkah-langkah yang tersisa dalam tutorial ini berlangsung dalam konteks kontainer pengembangan ini.

Menyebarkan sampel

  1. Masuk ke Azure.

    azd auth login
    
  2. Provisikan sumber daya dan sebarkan sampel ke lingkungan hosting.

    azd up
    

    Saat diminta, masukkan informasi berikut:

    Cepat Masuk
    Masukkan nama lingkungan yang unik secure-upload
    Pilih Langganan Azure yang akan digunakan Pilih langganan Anda dari daftar
    Masukkan nilai untuk parameter infrastruktur 'lokasi' Pilih dari lokasi yang tersedia

    Atau, jika Anda ingin melihat sumber daya yang disediakan lalu melihat output penyebaran, Anda dapat menjalankan perintah berikut untuk menyebarkan tanpa perintah:

    azd provision
    

    Kemudian jalankan perintah ini untuk menyebarkan kode aplikasi:

    azd deploy
    

    Jika Anda mengubah API atau kode aplikasi web, Anda dapat menyebarkan ulang kode aplikasi hanya dengan salah satu perintah berikut:

    azd deploy app
    azd deploy api
    
  3. Setelah penyebaran selesai, perhatikan URL aplikasi web yang disebarkan yang ditampilkan di terminal.

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

    Ini adalah contoh URL. URL Anda akan berbeda.

Coba sampel

  1. Buka aplikasi web yang disebarkan di tab browser baru dan pilih file PNG untuk diunggah. Beberapa file PNG tersedia di ./docs/media folder .

    Cuplikan layar aplikasi web untuk mengunggah file ke Azure Storage, memperlihatkan tombol Pilih File dan unggahan nama kontainer.

  2. Pilih Dapatkan token SAS, lalu pilih Unggah file.

  3. Lihat file yang Anda unggah di galeri di bawah tombol unggah.

    Cuplikan layar aplikasi web setelah mengunggah daisies.jpg ke Azure Storage, memperlihatkan nama file, URL SAS, status unggahan, dan gambar mini gambar.

Apa yang baru saja terjadi?

  • File Anda diunggah langsung dari browser ke Azure Storage menggunakan token SAS terbatas waktu dan hanya tulis
  • Gambar galeri dimuat langsung dari Azure Storage dengan menggunakan token SAS baca saja.
  • Tidak ada rahasia autentikasi yang terekspos di browser Anda

Cara kerja kode

Sekarang setelah Anda melihat aplikasi beraksi, jelajahi cara kode menerapkan unggahan file yang aman. Aplikasi ini memiliki dua bagian utama:

  1. Backend API - Mengautentikasi dengan Azure dan menghasilkan token SAS
  2. Frontend React - Mengunggah file langsung ke Azure Storage menggunakan token SAS

Bagian berikut menelusuri implementasi kode kunci.

Server API untuk menghasilkan token SAS dan mencantumkan file

Server API mengautentikasi ke Azure Storage dan menghasilkan token SAS terbatas waktu untuk digunakan browser.

Autentikasi dengan Identitas Terkelola

Aplikasi ini menggunakan Kunci Delegasi Pengguna dengan Identitas Terkelola untuk autentikasi, yang merupakan pendekatan paling aman untuk aplikasi Azure. ChainedTokenCredential mencoba metode autentikasi dalam urutan ini:

  1. Di Azure: ManagedIdentityCredential (Identitas Aplikasi Kontainer)
  2. Pengembangan lokal: AzureCliCredential (sesi Anda 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;
}

Setelah autentikasi, buat BlobServiceClient untuk berinteraksi dengan Azure Storage:

// 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);
}

Membuat token SAS dengan Kunci Delegasi Pengguna

Token SAS memerlukan Kunci Delegasi Pengguna, yang mengautentikasi token menggunakan kredensial ID Microsoft Entra alih-alih kunci akun penyimpanan. Kunci ini valid untuk rentang waktu tertentu:

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

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

Menghasilkan token SAS tulis-saja untuk unggahan file

Untuk unggahan file, API menghasilkan token tulis-saja yang tidak dapat membaca atau menghapus data. Token kedaluwarsa setelah 10 menit:

// 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}`;

Tingkat izin yang tersedia:

  • 'r' - Baca (unduh/lihat)
  • 'w' - Tulis (unggah/timpa) - Digunakan untuk unggahan
  • 'd' -Menghapus
  • 'c' -Membuat
  • 'a' - Tambahkan (lampirkan blob)

Membuat token SAS baca-saja untuk mencantumkan dan menampilkan file

Untuk mencantumkan dan menampilkan file, API menghasilkan token baca-saja yang kedaluwarsa setelah 60 menit:

// 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}`;

Permintaan klien aplikasi web dan menerima token SAS dari server API

Frontend React meminta token SAS dari API dan menggunakannya untuk langsung mengunggah file ke Azure Storage dari browser.

Frontend mengikuti proses tiga langkah:

  1. Meminta token SAS dari API untuk file tertentu
  2. Mengunggah langsung ke Azure Storage menggunakan URL token SAS
  3. Mengambil dan menampilkan daftar file yang diunggah dengan token SAS baca-saja

Arsitektur ini menjaga backend tetap ringan - hanya menghasilkan token, tidak pernah menangani data file.

Meminta Token SAS Blob Storage dari server API

Saat pengguna memilih file dan mengklik "Dapatkan Token SAS", frontend meminta token SAS tulis-saja dari 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
    });
};

Apa yang terjadi:

  • Frontend mengirimkan: GET /api/sas?file=photo.jpg&permission=w&container=upload&timerange=10
  • API mengembalikan: { url: "https://storageaccount.blob.core.windows.net/upload/photo.jpg?sv=2024-05-04&..." }
  • URL ini berlaku selama 10 menit dan memberikan akses tulis-saja ke blob tertentu

Mengunggah langsung ke Blob Storage menggunakan token SAS

Setelah URL token SAS diterima, frontend mengonversi file ke ArrayBuffer dan mengunggah file langsung ke Azure Storage - melewati API sepenuhnya. Ini mengurangi beban server dan meningkatkan performa.

Mengonversi file ke 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);
  });
}

Kemudian, gunakan BlockBlobClient dari @azure/storage-blob untuk mengunggah data file menggunakan URL token 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);
    });
};

Poin-poin penting:

  • File tidak pernah melewati server API Anda
  • Unggahan langsung dari browser ke Azure Storage
  • Token SAS mengautentikasi permintaan
  • Tidak ada bandwidth server atau biaya pemrosesan untuk penanganan file

Ambil file langsung dari Azure Storage dan tampilkan gambar mini

Setelah unggahan berhasil, frontend mengambil daftar semua file dalam kontainer. Setiap file dalam daftar dilengkapi dengan token SAS baca-saja sendiri:

// 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
  });

Contoh respons:

{
  "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&..."
  ]
}

Frontend menggunakan URL SAS langsung dalam tag gambar. Browser mengambil gambar dari Azure Storage menggunakan token baca-saja yang disematkan:

// 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>

Cara kerjanya:

  • Setiap URL dalam daftar menyertakan token SAS hanya-baca (sp=r)
  • Browser membuat permintaan GET langsung ke Azure Storage
  • Tidak diperlukan autentikasi - token ada di URL
  • Token kedaluwarsa setelah 60 menit (dikonfigurasi dalam API)

Membersihkan sumber daya

Setelah selesai dengan tutorial ini, hapus semua sumber daya Azure untuk menghindari biaya berkelanjutan.

azd down

Pemecahan masalah

Laporkan masalah dengan sampel ini di repositori GitHub. Sertakan hal-hal berikut dengan masalah:

  • URL dari artikel
  • Langkah atau konteks dalam artikel yang bermasalah
  • Lingkungan pengembangan Anda

Contoh kode

Langkah selanjutnya

Sekarang setelah Anda mempelajari cara mengunggah file dengan aman ke Azure Storage, jelajahi topik terkait ini: