.NET Core 批量重置 Azure Blob Storage 的 mime type

2019-07-09 10:10:28 浏览数 (1)

我的博客使用 Azure Blob Storage 存储文章配图,结果今天玩 Azure CDN 的时候爆了,原因是图片mime type不对。我们来看看如何在 .NET Core 里批量重置 Azure Blob Storage 中文件的mime type吧。

起因

使用编程方式(Azure Storage API)上传的文件,如果不指定 ContentType ,那么 Azure 不会聪明到根据文件拓展名设置 ContentType 。这个 ContentType 最终就是输出给浏览器的HTTP Header中的content-type,即Web服务器上的mime type。对于没有设置 ContentType 的文件,直接访问 Azure Blob 的地址,会返回"application/octet-stream"。不同浏览器对此处理方式不一样,大部分浏览器会调用文件下载,而不是打开文件。于是,图片就没法显示了。

我博客中的配图,以前之所以没问题,是因为没有使用CDN让客户端直接读取图片,而是通过后台处理,会自动加上正确的mime type,因此这个问题一直没暴露。

获取文件的 ContentType

.NET Core 没有 MimeMapping.GetMimeMapping() 这个API,因此我们需要一个workaround。

感谢长沙.NET技术社区成员 @刘命汉 的发现以及 @周杰 的验证,ASP.NET Core 自带的 FileExtensionContentTypeProvider 是个可替代方案。

var pvd = new FileExtensionContentTypeProvider();

bool isKnownType = pvd.TryGetContentType("test.png", out string mimeType);

// mimeType: "image/png"

对于不认识的拓展名,TryGetContentType() 会返回 false | null

CloudBlockBlob 不设置 ContentType 的话会返回默认的 application/octet-stream,因此null不影响。

更改文件的 ContentType

对于已经上传到 Azure Blob Storage 的文件,可以通过编程方式更改 ContentType 。获取到 CloudBlockBlob 对象以后,设置 Properties.ContentType,然后调用 SetPropertiesAsync() 方法保存更改即可。

对于未上传到Azure的文件,设置完 ContentType 以后,不需要调用 SetPropertiesAsync(), 上传操作 UploadFromStreamAsync() 会带上这些属性。

参见我博客代码commit: https://github.com/EdiWang/Moonglade/commit/3508e35055ae33b2c2241d93f615283a109bad85

自制开源工具

我今天抽空写了个批量重置 Azure Blob Storage 文件Mime Type 的工具,已在 GitHub 开源:

https://github.com/EdiWang/Azure-Blob-MimeType-Reset

关键代码

获取 CloudBlobContainer

有了 CloudBlobContainer 才能遍历里面的文件

private static CloudBlobContainer GetBlobContainer()

{

CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(Options.AccountName, Options.AccountKey), true);

CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

CloudBlobContainer container = blobClient.GetContainerReference(Options.ContainerName);

return container;

}

修改ContentType

此处我做了个判断,只有 ContentType 不正确的文件,才重置 ContentType

private static CloudBlockBlob TrySetContentType(CloudBlockBlob blob, string contentType)

{

if (blob.Properties.ContentType.ToLower() != contentType)

{

blob.Properties.ContentType = contentType;

return blob;

}

return null;

}

遍历文件及提交更改

var pvd = new FileExtensionContentTypeProvider();

WriteMessage($"[{DateTime.Now}] Updating Mime Type...");

int affectedFilesCount = 0;

foreach (var blob in BlobContainer.ListBlobs().OfType<CloudBlockBlob>())

{

string extension = Path.GetExtension(blob.Uri.AbsoluteUri).ToLower();

bool isKnownType = pvd.TryGetContentType(extension, out string mimeType);

if (isKnownType)

{

if (TrySetContentType(blob, mimeType) != null)

{

WriteMessage($"[{DateTime.Now}] Updating {blob.Uri.AbsoluteUri} => {mimeType}");

await blob.SetPropertiesAsync();

affectedFilesCount ;

}

}

}

0 人点赞