2010-11-03 7 views
3

Je construis une application qui doit permettre aux utilisateurs de télécharger de grandes images (jusqu'à environ 100 Mo) vers le service Windows Azure Blob Storage. Ayant lu Rob Gillen's excellent article sur l'optimisation du téléchargement de fichiers pour Windows Azure, j'ai emprunté son approche pour faire un téléchargement parallèle de blocs de fichiers, en utilisant la méthode CloudBlockBlob.PutBlock() dans une boucle Parallel.For (le code est disponible here). Le problème que j'ai est que chaque fois que j'essaie de télécharger un fichier, je reçois une exception "InvalidMd5" du storage client. Soupçonnant que le problème peut être dans le stockage de développement, j'ai également essayé d'exécuter le code contre mon compte de stockage Azure en direct, mais j'ai eu la même erreur. En regardant le trafic avec Fiddler, je vois que l'en-tête "Content-MD5" est réglé sur un hachage MD5 valide. La description de l'erreur indique que "La valeur MD5 spécifiée dans la requête est invalide La valeur MD5 doit être 128 bits et codée en Base64", mais au meilleur de ma connaissance, la valeur que je vois être envoyée dans Fiddler est valide (par exemple a91c588092cedbdb1b82c2d3786fd509).Comment résoudre une erreur InvalidMd5 renvoyée par le service Windows Azure Blob Storage?

Voici le code que j'utilise pour le calcul du hachage (avec la permission de Rob Gillen):

public static string GetMD5HashFromStream(byte[] data) 
{ 
    MD5 md5 = new MD5CryptoServiceProvider(); 
    byte[] retVal = md5.ComputeHash(data); 

    StringBuilder sb = new StringBuilder(); 
    for (int i = 0; i < retVal.Length; i++) 
    { 
     sb.Append(retVal[i].ToString("x2")); 
    } 
    return sb.ToString(); 
} 

Et ceci est l'appel réel à PutBlock():

blob.PutBlock(transferDetails[j].BlockId, new MemoryStream(buff), blockHash, options); 

J'ai essayé aussi passer le hachage comme ceci:

Convert.ToBase64String(Encoding.UTF8.GetBytes(blockHash)) 

mais le résultat était le même - "InvalidMd5 "erreur :(

Le hachage MD5 est passé à PutBlock() avec un codage base64 (par ex. YTkxYzU4ODA5MmNlZGJkYjFiODJjMmQzNzg2ZmQ1MDk =) et sans (par exemple a91c588092cedbdb1b82c2d3786fd509) ne semble pas faire de différence.

Rob code évidemment travaillé pour lui et je n'ai vraiment aucune idée de ce qui peut causer le problème dans mon cas. Le seul changement que j'ai apporté au code de Rob est de modifier la méthode d'extension ParallelUpload() pour prendre un Stream au lieu d'un nom de fichier et de déterminer dynamiquement la taille du bloc en fonction de la taille du fichier téléchargé.

S'il vous plaît, si quelqu'un a une idée de la façon de résoudre ce problème, faites le moi savoir! Je serai vraiment reconnaissant! J'ai déjà perdu deux jours en luttant avec ça.

Répondre

3

Rob, merci d'offrir de l'aide et en soulignant la différence dans les hash MD5. Votre réponse m'a amené à penser dans la bonne direction. J'ai passé une autre journée entière à creuser dedans mais heureusement (et merci à votre remarque :)) j'ai finalement réussi à résoudre le problème. Il est apparu qu'il y avait en fait deux questions dans mon cas:

1) Le hachage MD5: J'ai remarqué le hachage que vous avez collé dans votre réponse est plus courte que celle que je recevais, mais il m'a fallu un certain temps pour voir le vôtre était exactement deux fois plus courte. Après quelques essais je découvert que le GetMD5HashFromStream() Méthode de votre application de test est de convertir le 16 octets hachage générée par le MD5CryptoServiceProvider à une chaîne de 32 caractères . Et c'était cette chaîne de 32 caractères qui causait le problème parce qu'il a été converti en Base64 et passé à la méthode PutBlock(), d'où le hachage deux fois plus long et donc invalide que le service de stockage blob se plaignait. Voici le code que je fini avec:

Original:

public static string GetMD5HashFromStream(byte[] data) 
{ 
    MD5 md5 = new MD5CryptoServiceProvider(); 
    byte[] retVal = md5.ComputeHash(data); 

    StringBuilder sb = new StringBuilder(); 
    for (int i = 0; i < retVal.Length; i++) 
    { 
     sb.Append(retVal[i].ToString("x2")); 
    } 
    return sb.ToString(); 
} 

et l'appel à PutBlock():

// calculate the block-level hash 
string blockHash = Helpers.GetMD5HashFromStream(buff); 
blob.PutBlock(transferDetails[j].BlockId, new MemoryStream(buff), blockHash, options); 

final:

MD5 md5 = new MD5CryptoServiceProvider(); 
byte[] blockHash = md5.ComputeHash(buff); 
string convertedHash = Convert.ToBase64String(blockHash, 0, 16); 
blob.PutBlock(transferDetails[j].BlockId, new MemoryStream(buff), convertedHash, options); 

Rob, je suis vraiment curieux de savoir comment le code a fonctionné dans votre cas et pourquoi pas dans le mien - est-ce quelque chose de spécifique à l'installation sur ma machine, ou peut-être une version différente des outils Azure (j'utilise v1.2) ... S'il vous plaît laissez-moi savoir si vous avez une idée.

2) Un bug dans le stockage de développement: beaucoup de passer au peigne fin le web m'a amené à this page qui mentionne un bogue obscur mais apparemment connu dans le stockage de développement:

Si deux requêtes tentent de télécharger un bloc à un blob qui n'a pas encore existe dans le stockage de développement, une demande crée le blob et le autre peut renvoyer le code d'état 409 (conflit), avec les services de stockage de BlobAlreadyExists de code d'erreur.

Voici ce que je suis venu avec de travailler autour:

public static bool IsDevelopmentStorageRunning() 
{ 
    return new Microsoft.ServiceHosting.Tools.DevelopmentStorage.DevStore().IsRunning(); 
} 

Vous devrez ajouter une référence à Microsoft.ServiceHosting.Tools.dll, qui était situé dans "C : \ Program Files \ Windows Azure SDK \ v1.2 \ bin "sur ma machine. , J'utilise cette méthode avant que la boucle Parallel.For qui traite les morceaux de fichiers comme suit:

bool isDevStorageRunning = StorageProxy.IsDevelopmentStorageRunning(); 
ParallelOptions parallelOptions = new ParallelOptions(); 
parallelOptions.MaxDegreeOfParallelism = isDevStorageRunning ? 1 : 4; 
Parallel.For(0, transferDetails.Length, parallelOptions, j => { ... }); 

J'espère que cela sauvera quelqu'un tous les tracas je suis passé par. Rob, merci encore une fois d'avoir aidé :)

3

tishon,

Après avoir vu ce post, je suis retourné et re-testé mon code, et je pense qu'il ya un problème avec les données passées (peut-être ce que vous êtes de passage dans la fonction?). Une chose qui m'a sauté immédiatement a été le hash MD5 que vous avez fourni ...dans tous les cas, je l'ai testé, mes hash md5 se terminent par deux égalent signes comme les suivants (capturé à partir Fiddler):

Content-MD5: D1Mxthoqhlwm9cC0729mWA ==

Je ne suis pas un expert Crypto, mais je Si vous avez des caractères invalides/dangereux dans votre ID blob avant de convertir une valeur codée en base64, vous obtiendrez des données non valides et des ID bloqués que Azure ne peut pas interpréter.

Si vous le souhaitez, vous pouvez m'envoyer un fichier de test qui provoque le problème rob sur gillenfamily .net et je serai heureux de le faire passer ... pourrait être en mesure de vous aider à le retrouver.

Rob

Questions connexes