Skip to content

Commit dd7c7ce

Browse files
author
Kendo Bot
committed
Sync with Kendo UI Professional
1 parent 7247665 commit dd7c7ce

File tree

7 files changed

+573
-1
lines changed

7 files changed

+573
-1
lines changed
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
---
2+
title: Azure Blob Storage
3+
page_title: Azure Blob Storage
4+
description: "Learn how to use the Telerik UI for {{ site.framework }} Upload component, to upload files to Azure Blob Storage"
5+
slug: azure_blob_storage_aspnetcore
6+
position: 3
7+
---
8+
9+
# Azure Blob Storage
10+
11+
The **Azure Blob Storage** is Microsoft's object storage solution for the cloud. The Blob storage is optimized for storing massive amounts of unstructured data (data that doesn't adhere to a particular data model or definition, such as text or binary data).
12+
13+
This article demonstrates how to upload images with the [Upload component]({% slug htmlhelpers_upload_aspnetcore %}) for {{ site.framework }} to Azure Blob Storage and view the uploaded images in a [{{ site.framework }} ListView]({% slug htmlhelpers_listview_aspnetcore %}).
14+
15+
## Prerequisites
16+
17+
To use {{ site.product }} with Azure Blob Storage, you need:
18+
19+
* [An Azure account]({% slug azure_getting_started_aspnetcore %})
20+
* [An Azure Storage Account](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-create?toc=%2Fazure%2Fstorage%2Fblobs%2Ftoc.json&tabs=azure-portal)
21+
* [An Azure Storage Container](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-portal#create-a-container)
22+
* [A web application configured to use {{site.product}} components](https://docs.telerik.com/{{site.platform}}/getting-started/first-steps)
23+
24+
## Setting up Container Permissions
25+
26+
After you create the Storage Container, set the permission level. This allows you to access the uploaded images:
27+
28+
1. Navigate to your storage account and select the container under **Data Storage** > **Containers**.
29+
1. Click on **Change access level** and select the desired access level:
30+
31+
![Access Level](../images/azure-blob-storage-container-access.png)
32+
33+
## Configuring the Web Application
34+
35+
Add the Azure account and the Azure Storage Container details to the `appsettings.json` of the application as they will be used to access the Container.
36+
37+
```
38+
{
39+
"ApplicationInsights": {
40+
"InstrumentationKey": ""
41+
},
42+
"Logging": {
43+
"IncludeScopes": false,
44+
"LogLevel": {
45+
"Default": "Debug",
46+
"System": "Information",
47+
"Microsoft": "Information"
48+
}
49+
},
50+
"AzureStorageConfig": {
51+
"AccountName": "<Your Account Name>",
52+
"AccountKey": "<Your Account Key>",
53+
"ImageContainer": "<Name of the created container>",
54+
"ConnectionString": "<Connention string>"
55+
}
56+
}
57+
```
58+
59+
>For clarity, this tutorial demonstrates how to store the endpoint URI and account Key in the `appsettings.json`. For production application hosted on Azure, it is strongly recommended to store sensitive data in [Azure Key Vault](https://docs.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-6.0#secret-storage-in-the-production-environment-with-azure-key-vault).
60+
61+
You can find the account key and connection string by navigating to your storage account&mdash;under **Security + networking** > **Access Keys**
62+
63+
![Access Keys](../images/azure-blob-storage-access-keys.png)
64+
65+
## Adding the Upload and ListView on the Client
66+
67+
The following configuration demonstrates how to add an [Upload component]({% slug htmlhelpers_upload_aspnetcore %}) with enabled validation for `jpg` and `png` files. The Success event handler of the Upload is used to refresh the ListView.
68+
69+
```Razor
70+
<script type="text/x-kendo-tmpl" id="template">
71+
<div class="img">
72+
<img src="#:Url#" width="150" height="150" alt="#:Name# image" />
73+
<h4>#:Name#</h4>
74+
</div>
75+
</script>
76+
77+
@(Html.Kendo().ListView<ImageViewModel>()
78+
.Name("listView")
79+
.TagName("div")
80+
.ClientTemplateId("template")
81+
.DataSource(dataSource => dataSource
82+
.Ajax()
83+
.Read(read => read.Action("GetImageDetails", "Images"))
84+
.PageSize(5)
85+
)
86+
.Pageable(pageable => pageable
87+
.Refresh(true)
88+
.ButtonCount(5)
89+
.PageSizes(new[] { 5, 15, 21 })
90+
)
91+
)
92+
93+
@(Html.Kendo().Upload()
94+
.Name("files")
95+
.Async(a=>a
96+
.Save("Save","Images")
97+
.Remove("Remove","Images")
98+
.AutoUpload(false)
99+
)
100+
.Validation(validation => validation.AllowedExtensions(new string[]{".jpg", ".png"}))
101+
.Events(ev=>ev.Success("onSuccess"))
102+
)
103+
104+
<script>
105+
function onSuccess(e){
106+
var listView = $("#listView").getKendoListView();
107+
listView.dataSource.read();
108+
}
109+
</script>
110+
111+
<style>
112+
.k-listview-content{
113+
display:flex;
114+
flex-direction:row;
115+
gap:20px;
116+
}
117+
.img h4 {
118+
line-height: 1.1em;
119+
font-size: .9em;
120+
font-weight: normal;
121+
text-transform: uppercase;
122+
color: #999;
123+
}
124+
</style>
125+
```
126+
127+
## Configuring the Server-Side
128+
129+
The following server-side logic demonstrates the server-side configuration of the Upload and the Listview components:
130+
131+
* The `Save` and `Remove` endpoints of the Upload component demonstrate the configuration required to save uploaded images in the Azure Storage Container or remove them, respectively.
132+
* The `GetImageDetails` action method demonstrates the configuration for the ListView endpoint.
133+
134+
```ImagesController
135+
public class ImagesController : Controller
136+
{
137+
private readonly AzureStorageConfig storageConfig = null;
138+
139+
public ImagesController(IOptions<AzureStorageConfig> config)
140+
{
141+
storageConfig = config.Value;
142+
}
143+
144+
[HttpPost]
145+
public async Task<IActionResult> Save(ICollection<IFormFile> files)
146+
{
147+
bool isUploaded = false;
148+
149+
try
150+
{
151+
if (files.Count == 0)
152+
return BadRequest("No files received from the upload");
153+
154+
if (storageConfig.AccountKey == string.Empty || storageConfig.AccountName == string.Empty)
155+
return BadRequest("sorry, can't retrieve your azure storage details from appsettings.js, make sure that you add azure storage details there");
156+
157+
if (storageConfig.ImageContainer == string.Empty)
158+
return BadRequest("Please provide a name for your image container in the azure blob storage");
159+
160+
foreach (var formFile in files)
161+
{
162+
163+
if (formFile.Length > 0)
164+
{
165+
using (Stream stream = formFile.OpenReadStream())
166+
{
167+
isUploaded = await UploadFileToStorage(stream, formFile.FileName, storageConfig);
168+
}
169+
}
170+
}
171+
172+
if (isUploaded)
173+
{
174+
return new AcceptedResult();
175+
}
176+
else
177+
return BadRequest("Look like the image couldnt upload to the storage");
178+
}
179+
catch (Exception ex)
180+
{
181+
return BadRequest(ex.Message);
182+
}
183+
}
184+
185+
public static async Task<bool> UploadFileToStorage(Stream fileStream, string fileName,
186+
AzureStorageConfig _storageConfig)
187+
{
188+
// Create a URI to the blob
189+
Uri blobUri = new Uri("https://" +
190+
_storageConfig.AccountName +
191+
".blob.core.windows.net/" +
192+
_storageConfig.ImageContainer +
193+
"/" + fileName);
194+
195+
// Create StorageSharedKeyCredentials object by reading
196+
// the values from the configuration (appsettings.json)
197+
StorageSharedKeyCredential storageCredentials =
198+
new StorageSharedKeyCredential(_storageConfig.AccountName, _storageConfig.AccountKey);
199+
200+
// Create the blob client.
201+
BlobClient blobClient = new BlobClient(blobUri, storageCredentials);
202+
203+
204+
205+
var fileBytes = ReadFully(fileStream);
206+
var data = new BinaryData(fileBytes);
207+
208+
// Upload the file
209+
await blobClient.UploadAsync(data);
210+
211+
return await Task.FromResult(true);
212+
}
213+
214+
private static byte[] ReadFully(Stream input)
215+
{
216+
using (MemoryStream ms = new MemoryStream())
217+
{
218+
input.CopyTo(ms);
219+
return ms.ToArray();
220+
}
221+
}
222+
223+
[HttpPost]
224+
public async Task<ActionResult> Remove(string[] fileNames)
225+
{
226+
// The parameter of the Remove action must be called "fileNames"
227+
228+
if (fileNames != null)
229+
{
230+
foreach (var fullName in fileNames)
231+
{
232+
await RemoveFileFileFromStorage(fullName, storageConfig);
233+
}
234+
}
235+
236+
// Return an empty string to signify success
237+
return Content("");
238+
}
239+
240+
public static async Task<bool> RemoveFileFileFromStorage(string fileName,
241+
AzureStorageConfig _storageConfig)
242+
{
243+
// Create BlobServiceClient from the account URI
244+
BlobServiceClient blobServiceClient = new BlobServiceClient(_storageConfig.ConnectionString);
245+
246+
// Get reference to the container
247+
BlobContainerClient container = blobServiceClient.GetBlobContainerClient(_storageConfig.ImageContainer);
248+
249+
//Remove blob matching the fileName
250+
await container.DeleteBlobAsync(fileName, DeleteSnapshotsOption.None);
251+
252+
return await Task.FromResult(true);
253+
}
254+
255+
public async Task<ActionResult> GetImageDetails([DataSourceRequest] DataSourceRequest request)
256+
{
257+
var data = await GetUploadedImages(storageConfig);
258+
return Json(data.ToDataSourceResult(request));
259+
}
260+
261+
public static async Task<List<ImageViewModel>> GetUploadedImages(AzureStorageConfig _storageConfig)
262+
{
263+
List<ImageViewModel> images = new List<ImageViewModel>();
264+
265+
// Create BlobServiceClient from the account URI
266+
BlobServiceClient blobServiceClient = new BlobServiceClient(_storageConfig.ConnectionString);
267+
268+
// Get reference to the container
269+
BlobContainerClient container = blobServiceClient.GetBlobContainerClient(_storageConfig.ImageContainer);
270+
271+
if (container.Exists())
272+
{
273+
var data = container.GetBlobs();
274+
foreach (BlobItem blobItem in container.GetBlobs())
275+
{
276+
images.Add(new ImageViewModel() { Name = blobItem.Name, Url = container.Uri + "/" + blobItem.Name });
277+
}
278+
}
279+
280+
return await Task.FromResult(images);
281+
}
282+
}
283+
```
284+
```AzureStorageConfig
285+
public class AzureStorageConfig
286+
{
287+
public string AccountName { get; set; }
288+
public string AccountKey { get; set; }
289+
public string ImageContainer { get; set; }
290+
public string ConnectionString { get; set; }
291+
}
292+
```
293+
294+
## See Also
295+
296+
* [Connecting to Azure CosmosDB]({% slug azure_cosmosdb_aspnetcore %})

0 commit comments

Comments
 (0)