无法从 Azure 存储中正确下载文件,下载文件时数据也会丢失

Not able to properly download files from azure storage and data are lost too when downloading files

我在 Azure blob 存储上保存了 2 个文件:

  1. Abc.txt
  2. Pqr.docx

现在我想创建这 2 个文件的 zip 文件并允许用户下载。

我已将其保存在我的数据库 table 字段中,如下所示:

Document
Abc,Pqr

现在,当我点击下载时,我得到的文件如下所示,其中没有数据文件扩展名也丢失了以下:

我希望用户在下载 zip 文件时获得 zip 中的确切文件(.txt、.docx)

这是我的代码:

public ActionResult DownloadImagefilesAsZip()
{
    string documentUrl = repossitory.GetDocumentsUrlbyId(id);//output:Abc.txt,Pqr.Docx
          if (!string.IsNullOrEmpty(documentUrl))
            {
                string[] str = documentUrl.Split(',');
                if (str.Length > 1)
                {
                    using (ZipFile zip = new ZipFile())
                    {
                        int cnt = 0;
                        foreach (string t in str)
                        {
                            if (!string.IsNullOrEmpty(t))
                            {
                                Stream s = this.GetFileContent(t);
                                zip.AddEntry("File" + cnt, s);

                            }
                            cnt++;
                        }
                        zip.Save(outputStream);
                        outputStream.Position = 0;
                        return File(outputStream, "application/zip", "all.zip");
                    }
                }

}

  public Stream GetFileContent(string fileName)
        {
            CloudBlobContainer container = this.GetCloudBlobContainer();
            CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);
            var stream = new MemoryStream();
            blockBlob.DownloadToStream(stream);
            return stream;
        }

 public CloudBlobContainer GetCloudBlobContainer()
        {
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["StorageConnectionString"].ToString());
            CloudBlobClient blobclient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer blobcontainer = blobclient.GetContainerReference("Mystorage");
            if (blobcontainer.CreateIfNotExists())
            {
                blobcontainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
            }
            blobcontainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
            return blobcontainer;
        }

我希望在用户下载 zip 文件时下载相同的文件。

谁能帮我解决这个问题?

我看到有人用 ICSharpZip 库,看看这段代码

public void ZipFilesToResponse(HttpResponseBase response, IEnumerable<Asset> files, string zipFileName)
    {
        using (var zipOutputStream = new ZipOutputStream(response.OutputStream))
        {
            zipOutputStream.SetLevel(0); // 0 - store only to 9 - means best compression
            response.BufferOutput = false;
            response.AddHeader("Content-Disposition", "attachment; filename=" + zipFileName);
            response.ContentType = "application/octet-stream";

            foreach (var file in files)
            {
                var entry = new ZipEntry(file.FilenameSlug())
                {
                    DateTime = DateTime.Now,
                    Size = file.Filesize
                };
                zipOutputStream.PutNextEntry(entry);
                storageService.ReadToStream(file, zipOutputStream);
                response.Flush();
                if (!response.IsClientConnected)
                {
                   break;
                }
            }
            zipOutputStream.Finish();
            zipOutputStream.Close();
        }
        response.End();
    }

取自此处generate a Zip file from azure blob storage files

我不是网络开发人员,但希望这会有所帮助。这段代码是在一种方法中,我使用流将 blob 列表下载到 zip 文件存档中。文件列表在所有方向上都有斜线,所以这里有代码来解决这个问题,并确保我得到的 blob 引用有正确的文本(没有 URL,如果 blob 没有斜杠在 "folder") 中。

我怀疑您的问题不是在使用内存流或二进制写入器。特异性有时会有所帮助。祝你好运。

using (ZipArchive zipFile = ZipFile.Open(outputZipFileName, ZipArchiveMode.Create))
{
    foreach (string oneFile in listOfFiles)
    {
        //Need the filename, complete with relative path. Make it like a file name on disk, with backwards slashes.
        //Also must be relative, so can't start with a slash. Remove if found.
        string filenameInArchive = oneFile.Replace(@"/", @"\");
        if (filenameInArchive.Substring(0, 1) == @"\")
            filenameInArchive = filenameInArchive.Substring(1, filenameInArchive.Length - 1);

        //blob needs slashes in opposite direction
        string blobFile = oneFile.Replace(@"\", @"/");

        //take first slash off of the (folder + file name) to access it directly in blob storage
        if (blobFile.Substring(0, 1) == @"/")
            blobFile = oneFile.Substring(1, oneFile.Length - 1);

        var cloudBlockBlob = this.BlobStorageSource.GetBlobRef(blobFile);
        if (!cloudBlockBlob.Exists()) //checking just in case
        {
            //go to the next file
            //should probably trace log this 
            //add the file name with the fixed slashes rather than the raw, messed-up one
            //  so anyone looking at the list of files not found doesn't think it's because
            //  the slashes are different
            filesNotFound.Add(blobFile);
        }
        else
        {
            //blob listing has files with forward slashes; that's what the zip file requires
            //also, first character should not be a slash (removed it above)

            ZipArchiveEntry newEntry = zipFile.CreateEntry(filenameInArchive, CompressionLevel.Optimal);

            using (MemoryStream ms = new MemoryStream())
            {
                //download the blob to a memory stream
                cloudBlockBlob.DownloadToStream(ms);

                //write to the newEntry using a BinaryWriter and copying it 4k at a time
                using (BinaryWriter entry = new BinaryWriter(newEntry.Open()))
                {
                    //reset the memory stream's position to 0 and copy it to the zip stream in 4k chunks
                    //this keeps the process from taking up a ton of memory
                    ms.Position = 0;
                    byte[] buffer = new byte[4096];

                    bool copying = true;
                    while (copying)
                    {
                        int bytesRead = ms.Read(buffer, 0, buffer.Length);
                        if (bytesRead > 0)
                        {
                            entry.Write(buffer, 0, bytesRead);
                        }
                        else
                        {
                            entry.Flush();
                            copying = false;
                        }
                    }
                }//end using for BinaryWriter

            }//end using for MemoryStream

        }//if file exists in blob storage

    }//end foreach file

} //end of using ZipFileArchive

我注意到两件事:

  1. 一旦您读取了流中的 blob 内容,就不会将该流的位置重置为 0。因此,您的 zip 中的所有文件都是零字节。
  2. 调用 AddEntry 时,您可能希望在此处指定 blob 的名称,而不是 "File"+cnt

请看下面的代码。这是一个控制台应用程序,可创建 zip 文件并将其写入本地文件系统。

    static void SaveBlobsToZip()
    {
        string[] str = new string[] { "CodePlex.png", "DocumentDB.png" };
        var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
        var blobClient = account.CreateCloudBlobClient();
        var container = blobClient.GetContainerReference("images");
        using (var fs = new FileStream("D:\output.zip", FileMode.Create))
        {
            fs.Position = 0;
            using (var ms1 = new MemoryStream())
            {
                using (ZipFile zip = new ZipFile())
                {
                    int cnt = 0;
                    foreach (string t in str)
                    {
                        var ms = new MemoryStream();
                        container.GetBlockBlobReference(t).DownloadToStream(ms);
                        ms.Position = 0;//This was missing from your code
                        zip.AddEntry(t, ms);//You may want to give the name of the blob here.
                        cnt++;
                    }
                    zip.Save(ms1);
                }
                ms1.Position = 0;
                ms1.CopyTo(fs);
            }
        }
    }

更新

这是 MVC 应用程序中的代码(尽管我不确定它是否是最好的代码:),但它可以工作)。我稍微修改了你的代码。

    public ActionResult DownloadImagefilesAsZip()
    {
        string[] str = new string[] { "CodePlex.png", "DocumentDB.png" }; //repossitory.GetDocumentsUrlbyId(id);//output:Abc.txt,Pqr.Docx
        CloudBlobContainer blobcontainer = GetCloudBlobContainer();// azureStorageUtility.GetCloudBlobContainer();
        MemoryStream ms1 = new MemoryStream();
        using (ZipFile zip = new ZipFile())
        {
            int cnt = 0;
            foreach (string t in str)
            {
                var ms = new MemoryStream();
                CloudBlockBlob blockBlob = blobcontainer.GetBlockBlobReference(t);
                blockBlob.DownloadToStream(ms);
                ms.Position = 0;//This was missing from your code
                zip.AddEntry(t, ms);//You may want to give the name of the blob here.
                cnt++;
            }
            zip.Save(ms1);
        }
        ms1.Position = 0;
        return File(ms1, "application/zip", "all.zip");
    }