You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Angular和Node下载Blob存储中的虚拟目录?探讨逐个下载Blob并打包Zip的可行性方案

实现Blob存储虚拟目录下载(Angular + Node.js)

Great question! This approach is fully feasible — in fact, it's the standard workaround for this exact scenario. Blob storage's "virtual directories" are just a logical abstraction (they don't exist as physical folders; blobs are grouped via naming prefixes like folder/subfolder/file.txt), so fetching all blobs under a specific prefix, zipping them, and serving the zip file is the reliable way to build directory download functionality.

分步实现方案

1. Node.js 后端(核心逻辑)

We'll use the @azure/storage-blob package to interact with blob storage, and archiver to create zip archives (we'll stream content directly to avoid storing all files on disk, which is memory-efficient for large directories).

Setup & Dependencies

First install the required packages:

npm install @azure/storage-blob archiver express cors

Core Code Example

const express = require('express');
const cors = require('cors');
const { BlobServiceClient } = require('@azure/storage-blob');
const archiver = require('archiver');
const app = express();

app.use(cors());
const PORT = 3000;

// Initialize Blob client (use your storage connection string or SAS token)
const blobServiceClient = BlobServiceClient.fromConnectionString('<YOUR_BLOB_STORAGE_CONNECTION_STRING>');
const containerName = '<YOUR_CONTAINER_NAME>';

app.get('/download-directory', async (req, res) => {
  const directoryPrefix = req.query.prefix; // e.g., 'project-docs/v1.0/'
  if (!directoryPrefix) {
    return res.status(400).send('Directory prefix is required');
  }

  try {
    const containerClient = blobServiceClient.getContainerClient(containerName);
    const zip = archiver('zip', { zlib: { level: 9 } }); // High compression

    // Set response headers for download
    res.setHeader('Content-Type', 'application/zip');
    res.setHeader('Content-Disposition', `attachment; filename="${directoryPrefix.replace(/\//g, '-')}.zip"`);

    // Pipe the zip stream directly to the response
    zip.pipe(res);

    // Fetch all blobs under the target prefix
    for await (const blob of containerClient.listBlobsFlat({ prefix: directoryPrefix })) {
      const blobClient = containerClient.getBlobClient(blob.name);
      const downloadStream = await blobClient.download();
      
      // Preserve the original directory structure in the zip
      const relativeFilePath = blob.name.substring(directoryPrefix.length);
      zip.append(downloadStream.readableStreamBody, { name: relativeFilePath });
    }

    // Finalize the zip archive
    await zip.finalize();
  } catch (error) {
    console.error('Error processing directory download:', error);
    res.status(500).send('Failed to download directory. Please try again later.');
  }
});

app.listen(PORT, () => {
  console.log(`Directory download service running on port ${PORT}`);
});

Key Notes

  • Use listBlobsFlat with a prefix to target all blobs in your virtual directory (including subdirectories)
  • Streaming avoids storing entire files on your server, making this approach scalable for large directories
  • We preserve the original folder structure by stripping the root prefix from blob names before adding them to the zip

2. Angular Frontend

Call the backend endpoint to trigger the download, and handle the blob response to save it locally.

Setup (Optional)

Install file-saver to simplify download handling:

npm install file-saver

Component Code

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { saveAs } from 'file-saver';

@Component({
  selector: 'app-directory-download',
  templateUrl: './directory-download.component.html'
})
export class DirectoryDownloadComponent {
  constructor(private http: HttpClient) {}

  downloadTargetDirectory(prefix: string) {
    const encodedPrefix = encodeURIComponent(prefix);
    const apiUrl = `http://localhost:3000/download-directory?prefix=${encodedPrefix}`;

    this.http.get(apiUrl, { responseType: 'blob' })
      .subscribe({
        next: (zipBlob) => {
          const zipFileName = `${prefix.replace(/\//g, '-')}.zip`;
          saveAs(zipBlob, zipFileName);
        },
        error: (err) => {
          console.error('Download failed:', err);
          alert('Oops! Something went wrong with your download.');
        }
      });
  }
}

Template Example

<button (click)="downloadTargetDirectory('project-docs/v1.0/')">
  Download Project Docs v1.0
</button>

Extra Optimization Tips

  • Error Handling: Add retry logic for failed blob downloads, and log issues instead of failing the entire zip
  • Security: Use SAS tokens instead of full connection strings if you want to limit backend permissions, or generate SAS tokens client-side and pass them to the backend
  • Progress Tracking: For large directories, add a progress bar by listening to the response's download progress event in Angular
  • Cleanup: If you ever need to store temporary files (avoided here via streaming), add logic to delete them after the download completes

内容的提问来源于stack exchange,提问作者sonuD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 23:47:34