我有一个(相当大的)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();
}
一切都很神秘;我认为我们用来计算阻止列表的算法应该产生长度相同的字符串......
答案 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时,也会发生此异常。