一,引言

  上一篇文章,我们介绍到在实际项目中系统会产生大量的日志文件,用户上传的头像等等,同时也介绍到可以使用Azure Blob Storage 来存储项目中的一些日志文件,用户头像,用户视频等等。这个时候,有人就会问到,那我大概明白Blob可以存放什么类型的数据了,这个时候就有人问一些问题了

  1,什么是Blob?

  答 Azure Blob 存储是非结构化的,它可以保存的数据类型是没有任何限制的。如,pdf文档,json文件,视频,txt文件等。我们常见的类型的文件,它统统可以进行存储。但是 Blob 不适合需要经常查询的结构化数据, 比如说项目中使用的,它们具有比内存和本地磁盘更高的延迟,并且没有可让数据库高效运行查询的索引功能。但是,blob经常用与数据库一起用于存储不可查询的的数据,例如用户头像存储在Blob中,数据库中保存对应用户头像的Blob名称或URL(来自微软官方解释

  其实用可以这么说,如果项目中有对于的图片资源,视频资源,文件等资源,我们就可以考虑到将这些数据都存储在Azure Blob中。今天在文章的后半段我们将通过一个简单的 .NET Core Web 程序去操作 Blob 存储对象。

二,正文

开始之前我们看看 Blob 的类型

  1,block blob(块 blob):由不同大小的块构成,在写入到块 blob 时,需要将数据上传到块并将其提交到 blob。

  2,append blob(追加 blob):是专用的块 blob,它仅支持追加新数据,追加操作非常高效。 追加 blob 非常适用于存储日志或写入流数据等方案。

  3,page blob(页 blob):专用于涉及随机存取读写的方案。 页 blob 用于存储 Azure 虚拟机所使用的虚拟硬盘 (VHD) 文件,但它们非常适用于任何涉及到随机存取的方案。

今天我们演示的是 block blob类似,实际项目中,我们是如何进行操作的

1,Azure Portal 上进行创建 blob 数据

找到之前创建好的 “cnbateblogaccount” Storage Account,点击图中圈起来的 “Containers”,进行创建容器

点击 “ + Container ”,进行创建容器

注意:这里的容器,我们可以理解为用来存储对象的容器

Name:“picturecontainer”

Public access level 选择:“Private(no anonymous access)”(私有的,不允许匿名访问)

点击 “Create"

创建完成后,我们就可以在当前容器页面看到自己创建的 ”picturecontainer“ 信息。

我们点击容器进入,尝试在上传一张图片进行测试

点击 “Select a file” 进行选择

选择 “background.jpg”,点击 "打开"

勾选 “Overwrite if file already exist”(如果文件存储,就覆盖)

点击 “Upload” 进行上传

我们可以看到上传的图片文件,并且它的 Blob 类型是 “block blob”

同时,我们可以还有在Portal 上进行预览,编辑操作。

ok,上述操作我们是在Azure Portal 上进行操作的。接下来我们通过代码进行 blob 的增删查

2,通过ASP.NET Core Web 项目进行Blob数据操作

vs2019 新建 Web 项目

2.1,首先得安装 Azure.Storage相关的包

NuGet:Azure.Storage.Blobs

我们创建  BlobExplorerController 控制器

[HttpGet("{BlobName}")]
public async Task<IActionResult> GetBlob(string blobName)
{
var data = await _blobService.GetBlobAsync(blobName);
return File(data.Content, data.ContentType);
}

2.2,创建 IBlobService 接口,和 BlobService 实现类,新增获取 Blob 的接口定义和具体实现方法

Task<BlobInfo> GetBlobAsync(string name);
#region 01,获取Blob,根据blob名称+async Task<BlobInfo> GetBlobAsync(string name)
/// <summary>
/// 获取Blob,根据blob名称
/// </summary>
/// <param name="name">blob名称</param>
/// <returns></returns>
public async Task<Azure.Storage.Models.BlobInfo> GetBlobAsync(string name)
{
var containerClient = _blobServiceClient.GetBlobContainerClient("picturecontainer"); var blobClient = containerClient.GetBlobClient(name);
var blobDownLoadInfo = await blobClient.DownloadAsync();
return new Azure.Storage.Models.BlobInfo(blobDownLoadInfo.Value.Content, blobDownLoadInfo.Value.ContentType);
}
#endregion

创建 BlobInfo 返回实体信息

public class BlobInfo
{
public BlobInfo(Stream content, string contentType)
{
this.Content = content;
this.ContentType = contentType;
}
public Stream Content { get; set; } public string ContentType { get; set; }
}

2.3 配置链接字符串

Azure Portal 中找到创建的 “cnbateaccount” 的Azure Blob Atorage,点击 “ Setting=> Access key”

复制图中的 “Connection string” 

将数据库链接字符串配置在项目的 appsettings 配置文件中

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"AzureBlobStorageConnectionString": "DefaultEndpointsProtocol=https;AccountName=cnbateblogaccount;AccountKey=FU01h022mn1JjONp+ta0DAXOO7ThK3dxxxxxxxxxxxxxxxxx891n9nycsTLGZF83nJpGvTIZvO5VCVxxxxxx0wndOOQ==;EndpointSuffix=core.windows.net"
}

2.4,注入 BlobService,BlobServiceClient

services.AddSingleton(x => new BlobServiceClient(Configuration.GetValue<string("AzureBlobStorageConnectionString")));

services.AddSingleton<IBlobSergvice, BlobService>();

Ok,我们启动项目进行测试

postman 中输入 :localhost:9001/Blobs/background.jpg,效果如下图所示:

浏览器访问:http://localhost:9001/Blobs/background.jpg,效果如下图所示:

ok,成功,成功获取到之前在 Portal 上传的图片

2.5 ,完善对 Blob 数据的新增,删除操作

BlobExplorerController 完整代码

 [Route("Blobs")]
public class BlobExplorerController : Controller
{
private readonly IBlobSergvice _blobService; public BlobExplorerController(IBlobSergvice blobService)
{
this._blobService = blobService;
} [HttpGet("{BlobName}")]
public async Task<IActionResult> GetBlob(string blobName)
{
var data = await _blobService.GetBlobAsync(blobName);
return File(data.Content, data.ContentType);
} [HttpGet("BlobsName")]
public async Task<IActionResult> ListBlobsName()
{
return Ok(await _blobService.ListBlobsNameAsync());
} [HttpPost("UploadFile")]
public async Task<IActionResult> UpLoadFile([FromBody] UploadFileRequest request)
{
await _blobService.UploadFileBlobAsync(request.FilePath, request.FileName);
return Ok();
} [HttpPost("UploadContent")]
public async Task<IActionResult> UploadContent([FromBody] UpLoadContentRequest request)
{
await _blobService.UploadContentBlobAsync(request.Content, request.FileName);
return Ok();
} [HttpDelete("{BlobName}")]
public async Task<IActionResult> DaleteFile(string blobName)
{
await _blobService.DeleteBlobAsync(blobName);
return Ok();
}
}

BlobExplorerController

IBlobSergvice 接口类

public interface IBlobSergvice
{
Task<BlobInfo> GetBlobAsync(string name); Task<IEnumerable<string>> ListBlobsNameAsync(); Task UploadFileBlobAsync(string filePath, string filename); Task UploadContentBlobAsync(string content, string filename); Task DeleteBlobAsync(string blobName); }

IBlobSergvice

BlobService 实现类

 public class BlobService : IBlobSergvice
{
private readonly BlobServiceClient _blobServiceClient; public BlobService(BlobServiceClient blobServiceClient)
{
this._blobServiceClient = blobServiceClient;
} #region 01,获取Blob,根据blob名称+async Task<BlobInfo> GetBlobAsync(string name)
/// <summary>
/// 获取Blob,根据blob名称
/// </summary>
/// <param name="name">blob名称</param>
/// <returns></returns>
public async Task<Azure.Storage.Models.BlobInfo> GetBlobAsync(string name)
{
var containerClient = _blobServiceClient.GetBlobContainerClient("picturecontainer"); var blobClient = containerClient.GetBlobClient(name);
var blobDownLoadInfo = await blobClient.DownloadAsync();
return new Azure.Storage.Models.BlobInfo(blobDownLoadInfo.Value.Content, blobDownLoadInfo.Value.ContentType);
}
#endregion #region 02,获取所有Blob名称+async Task<IEnumerable<string>> ListBlobsNameAsync()
/// <summary>
/// 获取所有Blob名称
/// </summary>
/// <returns></returns>
public async Task<IEnumerable<string>> ListBlobsNameAsync()
{
var containerClient = _blobServiceClient.GetBlobContainerClient("picturecontainer");
var items = new List<string>(); await foreach (var blobItem in containerClient.GetBlobsAsync())
{
items.Add(blobItem.Name);
}
return items;
}
#endregion #region 03,上传图片流,根据文件路径和文件名称+async Task UploadFileBlobAsync(string filePath, string filename)
/// <summary>
/// 上传图片流,根据文件路径和文件名称
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="filename">文件名称</param>
/// <returns></returns>
public async Task UploadFileBlobAsync(string filePath, string filename)
{
var containerClient = _blobServiceClient.GetBlobContainerClient("picturecontainer");
var blobClient = containerClient.GetBlobClient(filename);
await blobClient.UploadAsync(filePath, new BlobHttpHeaders { ContentType = filePath.GetContentType() });
}
#endregion #region 04,上传文件流,根据文件内容和文件名称+async Task UploadContentBlobAsync(string content, string filename)
/// <summary>
/// 上传文件流,根据文件内容和文件名称
/// </summary>
/// <param name="content">文件内容</param>
/// <param name="filename">文件名称</param>
/// <returns></returns>
public async Task UploadContentBlobAsync(string content, string filename)
{
var containerClient = _blobServiceClient.GetBlobContainerClient("picturecontainer");
var blobClient = containerClient.GetBlobClient(filename);
var bytes = Encoding.UTF8.GetBytes(content);
await using var memoryStream = new MemoryStream(bytes);
await blobClient.UploadAsync(memoryStream, new BlobHttpHeaders() { ContentType = filename.GetContentType() });
}
#endregion #region 05,删除Blob+async Task DeleteBlobAsync(string blobName)
/// <summary>
/// 删除Blob
/// </summary>
/// <param name="blobName">blob名称</param>
/// <returns></returns>
public async Task DeleteBlobAsync(string blobName)
{
var containerClient = _blobServiceClient.GetBlobContainerClient("picturecontainer");
var blobClient = containerClient.GetBlobClient(blobName);
await blobClient.DeleteIfExistsAsync();
}
#endregion
}

BlobService

UpLoadContentRequest

 public class UpLoadContentRequest
{
/// <summary>
/// 文件内容
/// </summary>
public string Content { get; set; } /// <summary>
/// 文件名称
/// </summary>
public string FileName { get; set; }
}

UpLoadContentRequest

UploadFileRequest

 public class UploadFileRequest
{
/// <summary>
/// 文件路径
/// </summary>
public string FilePath { get; set; } /// <summary>
/// 文件名称
/// </summary>
public string FileName { get; set; }
}

UploadFileRequest

FileExtensions 扩展类

 public static class FileExtensions
{
private static readonly FileExtensionContentTypeProvider provider = new FileExtensionContentTypeProvider(); public static string GetContentType(this string fileName)
{
if (!provider.TryGetContentType(fileName, out var contentType))
{
contentType = "application/octet-stream";
}
return contentType;
}
}

FileExtensions

目前我们分别添加了 上传图片接口,上传文件接口,删除文件接口

我们分别在 postman 中进行测试

(1)上传图片

FilePath(文件路径):”C:\\Users\\admin\\Desktop\\2020904001.jpg“

FileName(文件名称):”2020904001.jpg“

点击 ”Send“,我们可以看到响应返回状态码(Status) ”200 OK“

同时在 Portal 上也可以看到刚刚选择的 ”20200904001.jpg“ 文件

这里注意一下,为什么我们制定文件的路径,和名称就可以上传文件,并且可以在浏览器中查看,那是因为我们在上传文件中设置文件的请求头的原因

private static readonly FileExtensionContentTypeProvider provider = new FileExtensionContentTypeProvider();

        public static string GetContentType(this string fileName)
{
if (!provider.TryGetContentType(fileName, out var contentType))
{
contentType = "application/octet-stream";
}
return contentType;
}
public async Task UploadFileBlobAsync(string filePath, string filename)
{
var containerClient = _blobServiceClient.GetBlobContainerClient("picturecontainer");
var blobClient = containerClient.GetBlobClient(filename);
await blobClient.UploadAsync(filePath, new BlobHttpHeaders { ContentType = filePath.GetContentType() });
}

(2)上传json文件

Content:"{ \"Name\" : \"zhangsan\", \"Reamrk\" : \"This is work!!!\" }"

FileName:”log.json“

点击 ”Send“,上传 Json 文件

我们继续在 Portal 上进行查看上传的 json 文件

(3)查看 Blob 信息

接下来还是删除 Blob 数据的操作,我这里就不做演示了,大家可以自己调用接口进行测试

好的,那今天的内容就先到此结束,今天的目的只是通过简单的demo代码去演示如果操作 Azure Blob 数据,简单的跑跑代码,在.NET Core 中的demo代码是怎么去写的,具体的Azure.Storage 类中的方法,参数等今天的内容暂时不去解释。

鼓掌,撒花

三,结尾

今天的文章大概介绍了在 Portal 上如何上传 blob 图片文件,以及写了一个简单的 .NET Core demo 程序,通过代码我们我们去控制 Blob 数据的增删改查,下一篇继续介绍 Azure Storage 在代码中的操作,也着重讲一下Azure.Storage 中的类,以及方法的具体调用和说明。

github:https://github.com/yunqian44/Azure.Storage.git

作者:Allen

版权:转载请在文章明显位置注明作者及出处。如发现错误,欢迎批评指正。

Azure Storage 系列(二) .NET Core Web 项目中操作 Blob 存储的更多相关文章

  1. 在.net core web 项目中操作MySql数据库(非ORM框架,原生sql语句方式)

    本案例通过MySql.Data和Dapper包执行原生sql,实现对数据库的操作. 操作步骤: 第1步:在MySql数据库中新建表User(使用Navicat For MySql工具) 建表语句: c ...

  2. C# 动态创建SQL数据库(二) 在.net core web项目中生成二维码 后台Post/Get 请求接口 方式 WebForm 页面ajax 请求后台页面 方法 实现输入框小数多 自动进位展示,编辑时实际值不变 快速掌握Gif动态图实现代码 C#处理和对接HTTP接口请求

    C# 动态创建SQL数据库(二) 使用Entity Framework  创建数据库与表 前面文章有说到使用SQL语句动态创建数据库与数据表,这次直接使用Entriy Framwork 的ORM对象关 ...

  3. 在.net core web 项目中使用Nlog记录日志

    第1步,添加NLog.Web.AspNetCore包引用 方法1 在项目上右击“依赖项”---“管理Nuget程序包(N)…”,然后在浏览对话框中输入“NLog.Web.AspNetCore”查找包, ...

  4. 在.net core web项目中生成图片验证码

    第1步:添加SkiaSharp包引用 Install-Package SkiaSharp 第2步:编写生成图片验证码的代码 using SkiaSharp; //在类文件头部添加引用 public I ...

  5. 在.net core web项目中生成二维码

    1.添加QRCoder包引用 2. public IActionResult MakeQrCode()        { string url="https://www.baidu.com& ...

  6. logback系列二:logback在项目中的应用

    1.输出http日志 2.输出dubbo日志 3.输出interfacer日志 4.输出到access,remote,app等目录中

  7. Azure Storage 系列(四)在.Net 上使用Table Storage

    一,引言 今天我们就不多说废话了,直接进入正题,Azure Table Storage.开始内容之前,我们先介绍一下Azure Table Storage. 1,什么是Azure Table Stor ...

  8. Azure Storage 系列(五)通过Azure.Cosmos.Table 类库在.Net 上使用 Table Storage

    一,引言 上一篇文章我们在.NET 项目中添加了 “WindowsAzure.Storage” 的 NuGet 包进行操作Table 数据,但是使用的 “WindowsAzure.Storage”  ...

  9. Azure Storage 系列(六)使用Azure Queue Storage

    一,引言 在之前介绍到 Azure Storage 第一篇文章中就有介绍到 Azure Storage 是 Azure 上提供的一项存储服务,Azure 存储包括 对象.文件.磁盘.队列和表存储.这里 ...

随机推荐

  1. Kaggle-pandas(4)

    Grouping-and-sorting 教程 映射使我们可以一次将整个列中的数据转换为DataFrame或Series中的一个值. 但是,通常我们希望对数据进行分组,然后对数据所在的组进行特定的操作 ...

  2. 前端面试基础题:Ajax原理

    Ajax 的原理简单来说是在⽤户和服务器之间加了—个中间层( AJAX 引擎),通过XmlHttpRequest 对象来向服务器发异步请求,从服务器获得数据,然后⽤ javascrip t 来操作 D ...

  3. 【av68676164(p43-p47)】物理内存管理

    7.2.1 分区内存管理 把内存分为若干个区给用户使用 单一区存储管理 分区存储管理 固定分区 动态分区 单一区存储管理(不分区存储管理) 定义:用户区不分区,完全被一个程序占用.例如:DOS 优点: ...

  4. 常哥带你认识NoSQL和Redis的强大

    各位朋友,这篇文章是针对Redis快速了解的内容,为了学好Redis在这里首先跟大家聊聊NoSQL相关内容,有了概念和方向后,我们再学习Redis大家会感觉得心应手. [公众号dotNet工控上位机: ...

  5. LeetCode 91,点赞和反对五五开,这题是好是坏由你来评判

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode专题的第57篇文章,我们一起来看看LeetCode第91题,解码方法(Decode ways). 这道题官方给定的难度 ...

  6. 年近而立,Java何去何从?

    摘要:各种新需求新应用层出不尽,各路新生代编程语言异军突起等等,Java未来会不会继续受欢迎?曾经备受关注的Java是否能紧跟时代步伐?新基建时代破茧重生引领云端未来潮流? 1995年第一个Java版 ...

  7. EXCEL 中数据 批量 填充进 word 中

    工具:Python3.7 需求描述:将EXCEL中 第二行 数据 填在 word 对应位置上,然后保存为 "姓名+任务.docx"文件. 再将EXCEL中 第三行 数据 填在 wo ...

  8. 你真的会做 2 Sum 吗?| 含双重好礼

    小预告:文末有两份福利,记得看到最后哦- 2 Sum 这题是 Leetcode 的第一题,相信大部分小伙伴都听过的吧. 作为一道标着 Easy 难度的题,它真的这么简单吗? 我在之前的刷题视频里说过, ...

  9. 源码解析JDK1.8-HashMap链表成环的问题解决方案

    前言 上篇文章详解介绍了HashMap在JDK1.7版本中链表成环的原因,今天介绍下JDK1.8针对HashMap线程安全问题的解决方案. jdk1.8 扩容源码解析 public class Has ...

  10. Cannot instantiate the type ......的解决

    使用public abstract class MainWindow implements ActionListener{} 之后创建对象MainWindow window = new MainWin ...