8

我有一个(相当大的)Azure 应用程序,它将(相当大的)文件并行上传到 Azure blob 存储。

在百分之几的上传中,我得到了一个例外:

The specified block list is invalid.

System.Net.WebException: The remote server returned an error: (400) Bad Request.

这是当我们运行一段看起来相当无害的代码来将 blob 并行上传到 Azure 存储时:

    public static void UploadBlobBlocksInParallel(this CloudBlockBlob blob, FileInfo file) 
    {
        blob.DeleteIfExists();
        blob.Properties.ContentType = file.GetContentType();
        blob.Metadata["Extension"] = file.Extension;

        byte[] data = File.ReadAllBytes(file.FullName);

        int numberOfBlocks = (data.Length / BlockLength) + 1;
        string[] blockIds = new string[numberOfBlocks];

        Parallel.For(
            0, 
            numberOfBlocks, 
            x =>
        {
            string blockId = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
            int currentLength = Math.Min(BlockLength, data.Length - (x * BlockLength));

            using (var memStream = new MemoryStream(data, x * BlockLength, currentLength))
            {
                var blockData = memStream.ToArray();
                var md5Check = System.Security.Cryptography.MD5.Create();
                var md5Hash = md5Check.ComputeHash(blockData, 0, blockData.Length);

                blob.PutBlock(blockId, memStream, Convert.ToBase64String(md5Hash));
            }

            blockIds[x] = blockId;
        });

        byte[] fileHash  = _md5Check.ComputeHash(data, 0, data.Length);
        blob.Metadata["Checksum"] = BitConverter.ToString(fileHash).Replace("-", string.Empty);
        blob.Properties.ContentMD5 = Convert.ToBase64String(fileHash);

        data = null;
        blob.PutBlockList(blockIds);
        blob.SetMetadata();
        blob.SetProperties();
    }

都非常神秘;我认为我们用来计算阻止列表的算法应该产生长度相同的字符串......

4

3 回答 3

6

我们遇到了类似的问题,但是我们没有指定任何块 ID,甚至没有在任何地方使用块 ID。在我们的例子中,我们使用的是:

using (CloudBlobStream stream = blob.OpenWrite(condition))
{
   //// [write data to stream]

   stream.Flush();
   stream.Commit();
}

这将导致The specified block list is invalid.并行负载下的错误。UploadFromStream(…)在将数据缓冲到内存中时切换此代码以使用该方法解决了该问题:

using (MemoryStream stream = new MemoryStream())
{
   //// [write data to stream]

   stream.Seek(0, SeekOrigin.Begin);
   blob.UploadFromStream(stream, condition);
}

显然,如果太多数据缓冲到内存中,这可能会对内存产生负面影响,但这是一种简化。需要注意的一点是,UploadFromStream(...)Commit()在某些情况下使用,但会检查其他条件以确定最佳使用方法。

于 2018-10-29T21:59:26.480 回答
2

注意:此解决方案基于 Azure JDK 代码,但我认为我们可以放心地假设纯 REST 版本将具有相同的效果(实际上与任何其他语言一样)。

由于我花了整个工作日来解决这个问题,即使这实际上是一个极端案例,我也会在这里留下一个便条,也许它会对某人有所帮助。

我做的一切都是正确的。我有正确顺序的块 ID,我有相同长度的块 ID,我有一个干净的容器,没有以前的一些块的剩余物(这三个原因是我能够通过谷歌找到的唯一原因)。

有一个问题:我一直在为提交构建我的阻止列表

CloudBlockBlob.commitBlockList(Iterable<BlockEntry> blockList)

使用此构造函数:

BlockEntry(String id, BlockSearchMode searchMode)

通过

BlockSearchMode.COMMITTED

在第二个论点。这证明是根本原因。一旦我将其更改为

BlockSearchMode.UNCOMMITTED

并最终落到单参数构造函数上

BlockEntry(String id)

默认情况下使用 UNCOMMITED,提交阻止列表有效并且 blob 成功持久化。

于 2020-01-29T20:15:19.577 回答
2

当多个线程将流打开到具有相同文件名的 blob 并尝试同时写入此 blob 时,也会发生此异常。

于 2019-04-29T11:48:11.540 回答