上传块Blob并设置内容类型

41

我正在使用C#中的Microsoft.WindowsAzure.Storage.*库.

这是我将东西上传到存储的方法:

// Store in storage
CloudStorageAccount storageAccount = CloudStorageAccount.Parse("...connection string...");
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("pictures");

// Create container if it doesnt exist
container.CreateIfNotExists();

// Make available to everyone
container.SetPermissions(new BlobContainerPermissions
{
    PublicAccess = BlobContainerPublicAccessType.Blob
});

// Save image
CloudBlockBlob blockBlob = container.GetBlockBlobReference("blah.jpg");
blockBlob.UploadFromByteArray(byteArrayThumbnail, 0, byteArrayThumbnail.Length);
blockBlob.Properties.ContentType = "image/jpg";  // *** NOT WORKING ***

我上传到存储中的所有内容都以"application/octet-stream"类型保存,尽管我使用具有"value"为"image/jpg"的setter(请参见我的代码中的最后一行)。

所以问题#1:为什么ContentType setter没有起作用?

问题#2:如果我手动将内容类型更改为"image/jpg",使用Windows Azure管理门户,然后将文件的绝对URI复制到浏览器的地址栏中,并按下回车键,则jpg文件会被下载而不是显示。这个MIME类型不应该被显示而不是下载吗?我该如何更改这个设置?

6个回答

116

你实际上不需要调用SetProperties方法。为了在上传Blob时设置内容类型,只需在调用上传方法之前设置ContentType属性即可。因此,你的代码应该是:

// Save image
CloudBlockBlob blockBlob = container.GetBlockBlobReference("blah.jpg");
blockBlob.Properties.ContentType = "image/jpg";
blockBlob.UploadFromByteArray(byteArrayThumbnail, 0, byteArrayThumbnail.Length);

那就可以搞定了。


哇!我会接受两个答案 :/ 无论如何,没有人回答我的第二个问题:当我在浏览器中输入URL时,它会将图像下载为文件而不是显示。尝试了image/jpeg和image/jpg。 - sports
实际上,我尝试上传一个JPG文件并设置其内容类型为image/jpg。当我将该URL放入浏览器中时,我能够在浏览器中看到该图像。我已经尝试了IE / Firefox / Chrome。您可以在Fiddler中跟踪请求/响应,并查看存储服务返回的实际内容类型吗? - Gaurav Mantri
你是通过SDK还是直接从Azure门户上传的?我会尝试跟踪。 - sports
2
实际上这并不是真的,我认为你看到这种行为是因为文件名带有扩展名。如果你使用一些其他标识符作为 Blob 名称(比如数据库键)或使用一个没有扩展名的名称,你就不会得到这种行为。 - Nick Albrecht
1
这不会设置内容类型。它可能对您起作用,因为 blob 名称中有 jpg 扩展名。 - Nagarjuna Yendluri
显示剩余3条评论

22

当你对Properties做出任何更改时,你需要调用CloudBlockBlob.SetProperties()来实际保存这些更改。

可以将其类比为LINQ-to-Entities。你可以随意更改本地对象,但在调用SaveChanges()之前,什么都不会被实际保存。


3
创建二进制大对象时应使用SetProperties方法更新而不是创建。 - AntonB
2
无论是第一次创建还是更新,都应该使用SetProperties。根据我的观察,如果您使用带有扩展名的文件名,则默认将ContentType设置为与扩展名匹配的内容类型。 如果您想确保它始终将其设置为您期望的内容类型,我建议(1)上传数据,(2)设置您的内容类型,(3)调用SetProperties()。https://azure.microsoft.com/zh-cn/documentation/articles/storage-properties-metadata/ - Nick Albrecht

20

使用新的SDK Azure.Storage.Blobs

BlobHttpHeaders blobHttpHeaders = new BlobHttpHeaders();
blobHttpHeaders.ContentType = "image/jpg";
blobClient.SetHttpHeaders(blobHttpHeaders);

3
如果Blob只包含一个部分,则使用BlockBlobClient.UploadAsync(Stream,BlobUploadOptions,CancellationToken)会导致崩溃:状态:404(指定的Blob不存在)。正确的方法是在调用UploadAsync之前设置BlobUploadOptions.Headers。 - Der_Meister

19

很遗憾,这里提供的所有答案都无法在最新的SDK(12.x.+)上工作。

使用最新的SDK时,应通过BlobHttpHeaders设置内容类型。

var _blobServiceClient = new BlobServiceClient("YOURCONNECTIONSTRING");

var containerClient = _blobServiceClient.GetBlobContainerClient("YOURCONTAINERNAME");

var blob = containerClient.GetBlobClient("YOURFILE.png");

var blobHttpHeader = new BlobHttpHeaders();

blobHttpHeader.ContentType = "image/png";

var uploadedBlob = await blob.UploadAsync(YOURSTREAM, blobHttpHeader);

适用于使用最新的Azure Blob SDK(v12及以上版本)的人们的完美解决方案。谢谢! - Yash Gupta
如何将此与“覆盖”现有文件结合使用?编辑:实际上,当您添加这些选项时,似乎不需要它。 - undefined

2

显然最好像Gaurav Mantri的回答那样在创建时设置它,如果你已经过了那个点并且需要更新其他答案,这里的其他答案可能会让你混乱。

// GET blob
CloudBlobContainer container = blobClient.GetContainerReference(containerName);
CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName); 

// if you don't do this you'll wipe properties you didn't mean to
await blockBlob.FetchAttributesAsync();

// SET
blockBlob.Properties.ContentType = mimetype;

// SAVE
await blockBlob.SetPropertiesAsync();

1

使用新版本的Azure Blob SDK,这个不再起作用。

对我来说,这个有效:

CloudBlockBlob blockBlob = cloudBlobContainer.GetBlockBlobReference(blobName);
blockBlob.Properties.ContentType = contentType;                            
await blockBlob.SetPropertiesAsync();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接