none
CloudBlockBlob.PutBlock() results in (400) Bad Request

    Question

  • I keep getting StorageExceptions when trying to use PutBlock to upload a file in chunks. The error message is "The remote server returned an error: (400) Bad Request."

    Here's an example of what I'm doing:

    public static void writeSegment(byte[] data, string contentMD5, string segmentId, string FileName, string ContainerName) {
    
        string error_message = "";
        CloudStorageAccount storageAccount = CloudStorageAccount.Parse("*****************");
        CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
        CloudBlobContainer container = blobClient.GetContainerReference(ContainerName);
        blockBlob = container.GetBlockBlobReference(FileName);
        string blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(segmentId));
    
        using (var chunkStream = new MemoryStream(data))
        {
            try
            {
                blockBlob.PutBlock(blockId, chunkStream, contentMD5, null, new BlobRequestOptions()
                {
                    RetryPolicy = new LinearRetry(TimeSpan.FromSeconds(10), 3)
                });
            }
            catch (StorageException se)
            {
                error_message = se.Message;
            }
        

    I know that I can write to my container because I can do this and it creates the blob fine:

    using (var fileStream = System.IO.File.OpenRead(@"C:\Users\whatever\test.jpg"))
    {
        blockBlob.UploadFromStream(fileStream);
    }

    I have been using this as a guide, but I have to say it's not quite as reliable as the name suggests. Parts of it need updating for 4.3, I think (especially the RetryPolicy bit; I've also tried null for the BlobRequestOptions, too). 

    I also tried null for the contentMD5 but it made no difference, and specifying "blockBlob.StreamWriteSizeInBytes = chunk.Length" also made no difference. The individual chunks are also fine (byte[] data) because I can append them all together and get the file back on my server (and the stream position is at 0, too). I'm running out of ideas :-(

    My FileName is just a Guid, which I know also works fine because I tried that with the UploadFromStream example to make sure Azure was happy with Guids as file names.
    • Edited by mfearby Wednesday, June 10, 2015 4:15 AM added bit about Guids
    Wednesday, June 10, 2015 4:12 AM

Answers

  • Hi mfearby,

    Please refer to https://msdn.microsoft.com/en-us/library/azure/dd135726.aspx for PutBlock API. Make sure your block id is less than or equal to 64 bytes before being encoded.

    Since I don't have idea on what kind of 400 error you're confronting, please try to print se.RequestInformation.ExtendedInformation.ErrorCode and se.RequestInformation.ExtendedInformation.ErrorMessage for the error details.

    Best Regards,

    Zhaoxing Lu

    • Marked as answer by mfearby Thursday, June 11, 2015 11:43 PM
    Thursday, June 11, 2015 2:46 PM

All replies

  • Hi,

    Please be advised that the Filename is case sensitive and needs to be in Lower Case.
    Also, have you tried changing the BlockID to GUID and checked if it works?

    Regards,
    Malar.

    Wednesday, June 10, 2015 11:18 AM
  • The FileName is just a Guid, which is already in lowercase: "0259626e-9125-45ec-86f5-92de752dab4f". I wouldn't think using the guid as the blockId is a good idea, though. Each individual block has to be individually (and sequentially!) identified, I would have thought.

    My blockId (before turning it into Base64) is "00001" and after "MDAwMDE="

    Wednesday, June 10, 2015 10:08 PM
  • If I set "contentMD5 = null" then my 5 separate calls to PutBlock() are working. I was then getting "400 Bad Request" errors with my PutBlockList(), which was being passed a List<string> with these five values: "00001", "00002", "00003", "00004", and "00005", but I found that I wasn't converting them to Base64, so it's all sorted now.

    So why can't I pass my own MD5 checksum like PutBlock() would have me believe that I should be able to The docs say:

    contentMD5
    An optional hash value used to ensure transactional integrity for the block. May be null or an empty string.

    Does "May be null or an empty string" mean that it can't actually contain a value (surely not!)? I'm comparing the MD5 generated in the browser with the computed hash in my web service, and they are identical, so I know the MD5 must be valid. I want to make sure that Azure's checksum is that same as mine so that I can be confident that each chunk uploads intact.

    Thanks.


    Thursday, June 11, 2015 1:46 AM
  • Hi mfearby,

    Please refer to https://msdn.microsoft.com/en-us/library/azure/dd135726.aspx for PutBlock API. Make sure your block id is less than or equal to 64 bytes before being encoded.

    Since I don't have idea on what kind of 400 error you're confronting, please try to print se.RequestInformation.ExtendedInformation.ErrorCode and se.RequestInformation.ExtendedInformation.ErrorMessage for the error details.

    Best Regards,

    Zhaoxing Lu

    • Marked as answer by mfearby Thursday, June 11, 2015 11:43 PM
    Thursday, June 11, 2015 2:46 PM
  • Thanks. My blockIds are 5 characters long, so they're fine. Thanks for the information about the exception detail (I wasn't drilling down that far). The error is: 

    ErrorCode: InvalidMd5
    ErrorMessage : The MD5 value specified in the request is invalid. MD5 value must be 128 bits and base64 

    Here are my MD5 checksums before and after converting to Base64 (both would exceed 128 bits):

    before: 243667f225fac2e5fe57ab3d515d8055
    after: MjQzNjY3ZjIyNWZhYzJlNWZlNTdhYjNkNTE1ZDgwNTU=

    Should the MD5 checksum be in another format before turning it into Base64? I'm using this method to compute the hashes.


    Thursday, June 11, 2015 10:58 PM
  • If I pass the result of this as the only argument to Convert.ToBase64String(), then I get an MD5 hash that Azure is happy with. I assumed I could use the hexadecimal representation of the 16-byte computed hash, but I was wrong.

    using (MD5 md5Hash = MD5.Create())
    {
        return md5Hash.ComputeHash(myByteArray);
    }
    

    Perhaps this could be pointed out in the documentation for PutBlock() to help others in future?

    Thanks for your help

    Thursday, June 11, 2015 11:43 PM