"指定的阻止列表无效"同时上传blob

时间:2012-10-16 15:11:47

标签: azure blobstorage

我有一个(相当大的)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();
    }

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

3 个答案:

答案 0 :(得分:2)

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

由于我花了整整一天的时间来解决这个问题,即使实际上这是一个极端的案例,我也会在这里留个字条,也许对某人会有帮助。

我做对了所有事情。我的区块ID顺序正确,我的ID长度相同,我的容器干净,没有以前的区块剩余(这三个原因是我可以通过Google找到的唯一原因)。

有一个陷阱:我一直在构建阻止列表以供通过

提交
CloudBlockBlob.commitBlockList(Iterable<BlockEntry> blockList)

使用此构造函数:

BlockEntry(String id, BlockSearchMode searchMode)

通过

BlockSearchMode.COMMITTED
在第二个参数中

。事实证明,是根本原因。一旦我将其更改为

BlockSearchMode.UNCOMMITTED

并最终登陆到一参数构造器上

BlockEntry(String id)

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

答案 1 :(得分:1)

我们遇到了类似的问题,但是我们没有指定任何块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(),但会检查其他条件以确定最佳的使用方法。

答案 2 :(得分:0)

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