實做壓縮儲存體並下載功能

  • 278
  • 0

前言

工作上剛好遇到需要將儲存體打包並下載的功能,所以整理了一下程式碼並寫成簡單範例。包含了列出儲存體容器內的檔案和打包下載的功能,壓縮的部分則是使用 SharpZipLib 套件來完成。

實做

準備儲存體和檔案

先建立好儲存體,並建立了一個容器名稱為 samplefile,並且上傳了幾個範例檔案。

取得儲存體的連線字串。

列出儲存體內檔案

新增一個 .NET 6 Razor Page 專案,並且新增 StorageKey 到設定檔內,值則為儲存體的連線字串

。新增相關 NuGet 套件:

新增 BlobFIleinfo Model ,作為之後取得儲存體的相關資訊用。

public class BlobFIleinfo
{
    /// <summary>
    /// File Name
    /// </summary>
    [Display(Name = "File Name")]
    public string Name { get; set; }
    /// <summary>
    /// File Size
    /// </summary>
    [Display(Name = "File Size")]
    public long? Size { get; set; }
    /// <summary>
    /// ContentType
    /// </summary>
    [Display(Name = "ContentType")]
    public string ContentType { get; set; }
}

新增 IStorageService 介面

public interface IStorageService
{
    /// <summary>
    /// Get Blob List
    /// </summary>
    /// <returns></returns>
        List<BlobFIleinfo> GetBlobList();
    /// <summary>
    /// Download Blob
    /// </summary>
    /// <param name="blobName">Blob Name</param>
    (byte[] File, string ContentType) DownloadBlob(string blobName);
    /// <summary>
    /// Zip Blobs
    /// </summary>
    /// <param name="fileNames">File Name</param>
    /// <returns></returns>
    byte[] ZipBlobs(List<string> fileNames);
}

實做 StorageService

/// <summary>
/// Storage Service
/// </summary>
public class StorageService : IStorageService
{
    private readonly BlobServiceClient _blobServiceClient;
    private const string containerName = "samplefile";
    public StorageService(IConfiguration configuration)
    {
        string connectionString = configuration.GetValue<string>("StorageKey");
        _blobServiceClient = new BlobServiceClient(connectionString);
    }
    /// <summary>
    /// Get Blob List
    /// </summary>
    /// <returns></returns>
    public List<BlobFIleinfo> GetBlobList()
    {
        var blobList = new List<BlobFIleinfo>();
        BlobContainerClient container = _blobServiceClient.GetBlobContainerClient(containerName);
        var blobPages = container.GetBlobs(BlobTraits.None).AsPages();

        foreach (var blobPage in blobPages)
        {
            foreach (BlobItem blobItem in blobPage.Values)
            {
                blobList.Add(new BlobFIleinfo()
                {
                    Name = blobItem.Name,
                    Size = blobItem.Properties.ContentLength,
                    ContentType = blobItem.Properties.ContentType
                });
            }
        }

        return blobList;
    }
    /// <summary>
    /// Download Blob
    /// </summary>
    /// <param name="blobName">Blob Name</param>
    public (byte[] File, string ContentType) DownloadBlob(string blobName)
    {
        BlobContainerClient container = _blobServiceClient.GetBlobContainerClient(containerName);
        BlobClient blob = container.GetBlobClient(blobName);

        if (blob.Exists())
        {
            MemoryStream file = new MemoryStream();
            BlobDownloadInfo download = blob.Download();
            download.Content.CopyTo(file);
            return (file.ToArray(), blob.GetProperties().Value.ContentType);
        }
        else
            return (null, null);
    }
    /// <summary>
    /// Zip Blobs
    /// </summary>
    /// <param name="fileNames">File Name</param>
    /// <returns></returns>
    public byte[] ZipBlobs(List<string> fileNames)
    {
        using (var output = new MemoryStream())
        using (var zipOutputStream = new ZipOutputStream(output))
        {
            zipOutputStream.SetLevel(9);
            foreach (var fileName in fileNames)
            {
                BlobContainerClient container = _blobServiceClient.GetBlobContainerClient(containerName);
                BlobClient blob = container.GetBlobClient(fileName);
                var entry = new ZipEntry(fileName);
                zipOutputStream.PutNextEntry(entry);
                blob.DownloadTo(zipOutputStream);
            }

            zipOutputStream.Finish();
            zipOutputStream.Close();

            return output.ToArray();
        }
    }
}

修改 Index 檔案

@page
@model IndexModel

<h1>Blob List</h1>

<div class="form-check">
    <form action="./ZipFile" method="get">
        <table class="table">
            <thead>
                <tr>
                    <th></th>
                    <th>
                        @Html.DisplayNameFor(model => model.BlobFIleinfos[0].Name)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.BlobFIleinfos[0].Size)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.BlobFIleinfos[0].ContentType)
                    </th>
                    <th></th>
                </tr>
            </thead>
            <tbody>
                @foreach (var item in Model.BlobFIleinfos)
                {
                    <tr>
                        <td>
                            <input name="fileName" class="form-check-input" type="checkbox" value="@item.Name" />
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Name)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Size)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.ContentType)
                        </td>
                        <td>
                            <a asp-page="./Download" asp-route-blobName="@item.Name">Download</a>
                        </td>
                    </tr>
                }
            </tbody>
        </table>
        <button type="submit" class="btn btn-primary">Zip Select Files</button>
    </form>
</div>

修改 Index.cshtml.cs 檔案

public class IndexModel : PageModel
{
    private readonly IStorageService _storageService;

    public IndexModel(IStorageService storageService)
    {
        _storageService = storageService;
    }

    public IActionResult OnGet()
    {
        BlobFIleinfos = _storageService.GetBlobList();
        return Page();
    }

    public List<BlobFIleinfo> BlobFIleinfos { get; set; }
}

新增 Download 頁面,因為沒有要呈現頁面,修改建立好的 Download.cshtml.cs 檔案

public class DownloadModel : PageModel
{
    private readonly IStorageService _storageService;

    public DownloadModel(IStorageService storageService)
    {
        _storageService = storageService;
    }

    public IActionResult OnGet(string blobName)
    {
        var blob = _storageService.DownloadBlob(blobName);
        return File(blob.File, blob.ContentType, blobName);
    }
}

新增 ZipFile 頁面,一樣修改 ZipFile.cshtml.cs 檔案即可

public class ZipFileModel : PageModel
{
    private readonly IStorageService _storageService;

    public ZipFileModel(IStorageService storageService)
    {
        _storageService = storageService;
    }

    public IActionResult OnGet(List<string> fileName)
    {
        var zipFile = _storageService.ZipBlobs(fileName);
        return File(zipFile, "application/octet-stream", "ZipFile.zip");
    }
}

到這邊就完成了所有功能,執行之後畫面如下呈現,會將儲存體內的檔案列出來,並且顯示檔案大小和類型,並且提供下載單一檔案和壓縮指定檔案功能。

完整程式碼可以到 GitHub 察看。

結論

目前功能就簡單實做了列表、單一檔案下載、打包選取下載,未來可以在加上更多功能,就可以實做出一個簡單的儲存體檔案總管功能。

參考資料