Your search did not match any results.
File Manager

Azure Client-Side Binding

This demo illustrates how to use the custom file provider to connect the FileManager component to the Azure Blob Storage on the client side. The Custom File System Provider allows you to implement custom APIs to handle file operations (add, delete, rename, etc.). All APIs that implement access to Azure Blob Storage on the client are stored in the azure.file.system.js file (app.service.ts - for Angular framework). On the server, configure the Shared Access Signature (SAS) to grant access to blobs in the storage.

Backend API
Copy to CodeSandBox
Apply
Reset
<div id="wrapper" [class]="wrapperClassName"> <dx-load-panel [position]="{ of: '#file-manager' }" [(visible)]="loadPanelVisible" > </dx-load-panel> <div id="widget-area"> <dx-file-manager id="file-manager" [fileSystemProvider]="fileSystemProvider" [allowedFileExtensions]="allowedFileExtensions" > <!-- uncomment the code below to enable file/directory management --> <!-- <dxo-permissions [create]="true" [copy]="true" [move]="true" [delete]="true" [rename]="true" [upload]="true" [download]="true"> </dxo-permissions> --> </dx-file-manager> <div id="request-panel"> <div class="request-info" *ngFor="let request of requests"> <div class="parameter-info"> <div class="parameter-name">Method:</div> <div class="parameter-value dx-theme-accent-as-text-color">{{ request.method }}</div> </div> <div class="parameter-info"> <div class="parameter-name">Url path:</div> <div class="parameter-value dx-theme-accent-as-text-color">{{ request.urlPath }}</div> </div> <div class="parameter-info"> <div class="parameter-name">Query string:</div> <div class="parameter-value dx-theme-accent-as-text-color">{{ request.queryString }}</div> </div> <br /> </div> </div> </div> <div id="message-box"> To run the demo locally, specify your Azure storage account name, access key and container name in the web.config file. Refer to the <a href="https://js.devexpress.com/Demos/WidgetsGallery/Demo/FileManager/AzureClientBinding/Angular/Light/" target="_blank" > https://js.devexpress.com/Demos/WidgetsGallery/Demo/FileManager/AzureClientBinding/Angular/Light/</a > to see the demo online. </div> </div>
import { NgModule, Component, enableProdMode } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { HttpClient, HttpClientModule } from '@angular/common/http'; import { lastValueFrom } from 'rxjs'; import { DxFileManagerModule, DxLoadPanelModule } from 'devextreme-angular'; import CustomFileSystemProvider from 'devextreme/file_management/custom_provider'; import { AzureGateway, AzureFileSystem } from './app.service'; if (!/localhost/.test(document.location.host)) { enableProdMode(); } @Component({ selector: 'demo-app', templateUrl: 'app/app.component.html', styleUrls: ['app/app.component.css'], preserveWhitespaces: true, }) export class AppComponent { allowedFileExtensions: string[]; fileSystemProvider: CustomFileSystemProvider; requests: any[]; wrapperClassName: string; loadPanelVisible: boolean; constructor(http: HttpClient) { const endpointUrl = 'https://js.devexpress.com/Demos/Mvc/api/file-manager-azure-access'; gateway = new AzureGateway(endpointUrl, this.onRequestExecuted.bind(this)); azure = new AzureFileSystem(gateway); this.allowedFileExtensions = []; this.fileSystemProvider = new CustomFileSystemProvider({ getItems, createDirectory, renameItem, deleteItem, copyItem, moveItem, uploadFileChunk, downloadItems, }); this.requests = []; this.wrapperClassName = ''; this.loadPanelVisible = true; this.checkAzureStatus(http); } onRequestExecuted({ method, urlPath, queryString }) { const request = { method, urlPath, queryString }; this.requests.unshift(request); } checkAzureStatus(http: HttpClient) { lastValueFrom(http.get<{ active: boolean }>('https://js.devexpress.com/Demos/Mvc/api/file-manager-azure-status?widgetType=fileManager')) .then((result) => { this.wrapperClassName = result.active ? 'show-widget' : 'show-message'; this.loadPanelVisible = false; }); } } function getItems(parentDirectory) { return azure.getItems(parentDirectory.path); } function createDirectory(parentDirectory, name) { return azure.createDirectory(parentDirectory.path, name); } function renameItem(item, name) { return item.isDirectory ? azure.renameDirectory(item.path, name) : azure.renameFile(item.path, name); } function deleteItem(item) { return item.isDirectory ? azure.deleteDirectory(item.path) : azure.deleteFile(item.path); } function copyItem(item, destinationDirectory) { const destinationPath = destinationDirectory.path ? `${destinationDirectory.path}/${item.name}` : item.name; return item.isDirectory ? azure.copyDirectory(item.path, destinationPath) : azure.copyFile(item.path, destinationPath); } function moveItem(item, destinationDirectory) { const destinationPath = destinationDirectory.path ? `${destinationDirectory.path}/${item.name}` : item.name; return item.isDirectory ? azure.moveDirectory(item.path, destinationPath) : azure.moveFile(item.path, destinationPath); } function uploadFileChunk(fileData, uploadInfo, destinationDirectory) { let promise = null; if (uploadInfo.chunkIndex === 0) { const filePath = destinationDirectory.path ? `${destinationDirectory.path}/${fileData.name}` : fileData.name; promise = gateway.getUploadAccessUrl(filePath).then((accessUrls) => { uploadInfo.customData.accessUrl = accessUrls.url1; }); } else { promise = Promise.resolve(); } promise = promise.then(() => gateway.putBlock(uploadInfo.customData.accessUrl, uploadInfo.chunkIndex, uploadInfo.chunkBlob)); if (uploadInfo.chunkIndex === uploadInfo.chunkCount - 1) { promise = promise.then(() => gateway.putBlockList(uploadInfo.customData.accessUrl, uploadInfo.chunkCount)); } return promise; } function downloadItems(items) { azure.downloadFile(items[0].path); } let gateway = null; let azure = null; @NgModule({ imports: [ BrowserModule, DxFileManagerModule, DxLoadPanelModule, HttpClientModule, ], declarations: [AppComponent], bootstrap: [AppComponent], }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
#widget-area { visibility: hidden; } #message-box { display: none; } .show-widget #widget-area { visibility: visible; } .show-message #widget-area { display: none; } .show-message #message-box { display: block; } #request-panel { min-width: 505px; height: 400px; overflow-x: hidden; overflow-y: auto; padding: 18px; margin-top: 40px; background-color: rgba(191, 191, 191, 0.15); } #request-panel .parameter-info { display: flex; } .request-info .parameter-name { flex: 0 0 100px; } .request-info .parameter-name, .request-info .parameter-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
export class AzureFileSystem { gateway: AzureGateway; EMPTY_DIR_DUMMY_BLOB_NAME: string; constructor(azureGateway) { this.gateway = azureGateway; this.EMPTY_DIR_DUMMY_BLOB_NAME = 'aspxAzureEmptyFolderBlob'; } getItems(path) { var prefix = this.getDirectoryBlobName(path); return this.gateway.getBlobList(prefix).then((entries) => this.getDataObjectsFromEntries(entries, prefix)); } createDirectory(path, name) { var blobName = path ? `${path}/${name}` : name; return this.gateway.createDirectoryBlob(blobName); } renameFile(path, name) { var newPath = this.getPathWithNewName(path, name); return this.moveFile(path, newPath); } renameDirectory(path, name) { var newPath = this.getPathWithNewName(path, name); return this.moveDirectory(path, newPath); } getPathWithNewName(path, name) { var parts = path.split('/'); parts[parts.length - 1] = name; return parts.join('/'); } deleteFile(path) { return this.gateway.deleteBlob(path); } deleteDirectory(path) { var prefix = this.getDirectoryBlobName(path); return this.executeActionForEachEntry(prefix, (entry) => this.gateway.deleteBlob(entry.name)); } copyFile(sourcePath, destinationPath) { return this.gateway.copyBlob(sourcePath, destinationPath); } copyDirectory(sourcePath, destinationPath) { var prefix = this.getDirectoryBlobName(sourcePath); var destinationKey = this.getDirectoryBlobName(destinationPath); return this.executeActionForEachEntry(prefix, (entry) => this.copyEntry(entry, prefix, destinationKey)); } copyEntry(entry, sourceKey, destinationKey) { var restName = entry.name.substr(sourceKey.length); var newDestinationKey = destinationKey + restName; return this.gateway.copyBlob(entry.name, newDestinationKey); } moveFile(sourcePath, destinationPath) { return this.gateway.copyBlob(sourcePath, destinationPath).then(() => this.gateway.deleteBlob(sourcePath)); } moveDirectory(sourcePath, destinationPath) { var prefix = this.getDirectoryBlobName(sourcePath); var destinationKey = this.getDirectoryBlobName(destinationPath); return this.executeActionForEachEntry(prefix, (entry) => this.copyEntry(entry, prefix, destinationKey).then(() => this.gateway.deleteBlob(entry.name))); } downloadFile(path) { this.gateway.getBlobUrl(path).then((accessUrls: AccessUrls) => { window.location.href = accessUrls.url1; }); } executeActionForEachEntry(prefix, action) { return this.gateway.getBlobList(prefix).then((entries) => { var deferreds = entries.map((entry) => action(entry)); return Promise.all(deferreds); }); } getDataObjectsFromEntries(entries, prefix) { var result = []; var directories = {}; entries.forEach((entry) => { var restName = entry.name.substr(prefix.length); var parts = restName.split('/'); if (parts.length === 1) { if (restName !== this.EMPTY_DIR_DUMMY_BLOB_NAME) { var obj = { name: restName, isDirectory: false, dateModified: entry.lastModified, size: entry.length, }; result.push(obj); } } else { var dirName = parts[0]; var directory = directories[dirName]; if (!directory) { directory = { name: dirName, isDirectory: true, }; directories[dirName] = directory; result.push(directory); } if (!directory.hasSubDirectories) { directory.hasSubDirectories = parts.length > 2; } } }); result.sort(this.compareDataObjects); return result; } compareDataObjects(obj1, obj2) { if (obj1.isDirectory === obj2.isDirectory) { var name1 = obj1.name.toLowerCase(); var name2 = obj2.name.toLowerCase(); if (name1 < name2) { return -1; } return name1 > name2 ? 1 : 0; } return obj1.isDirectory ? -1 : 1; } getDirectoryBlobName(path) { return path ? `${path}/` : path; } } export class AzureGateway { endpointUrl: any; onRequestExecuted: any; constructor(endpointUrl, onRequestExecuted) { this.endpointUrl = endpointUrl; this.onRequestExecuted = onRequestExecuted; } getBlobList(prefix) { return this.getAccessUrl('BlobList') .then((accessUrls: AccessUrls) => this.executeBlobListRequest(accessUrls.url1, prefix)) .then((xml) => this.parseEntryListResult(xml)); } parseEntryListResult(xmlString): Array<FileEntry> { var xml = new DOMParser().parseFromString(xmlString, 'text/xml'); return Array.from(xml.querySelectorAll('Blob')).map(this.parseEntry); } parseEntry(xmlEntry): FileEntry { var entry: FileEntry = {}; entry.etag = xmlEntry.querySelector('Etag').textContent; entry.name = xmlEntry.querySelector('Name').textContent; var dateStr = xmlEntry.querySelector('Last-Modified').textContent; entry.lastModified = new Date(dateStr); var lengthStr = xmlEntry.querySelector('Content-Length').textContent; entry.length = parseInt(lengthStr, 10); return entry; } executeBlobListRequest(accessUrl, prefix) { var params: RequestParams = { restype: 'container', comp: 'list', }; if (prefix) { params.prefix = prefix; } return this.executeRequest(accessUrl, params); } createDirectoryBlob(name) { return this.getAccessUrl('CreateDirectory', name).then( (accessUrls: AccessUrls) => this.executeRequest({ url: accessUrls.url1, method: 'PUT', headers: { 'x-ms-blob-type': 'BlockBlob', }, processData: false, contentType: false, }), ); } deleteBlob(name) { return this.getAccessUrl('DeleteBlob', name).then( (accessUrls: AccessUrls) => this.executeRequest({ url: accessUrls.url1, method: 'DELETE', }), ); } copyBlob(sourceName, destinationName) { return this.getAccessUrl('CopyBlob', sourceName, destinationName).then( (accessUrls: AccessUrls) => this.executeRequest({ url: accessUrls.url2, method: 'PUT', headers: { 'x-ms-copy-source': accessUrls.url1, }, }), ); } putBlock(uploadUrl, blockIndex, blockBlob) { var blockId = this.getBlockId(blockIndex); var params: RequestParams = { comp: 'block', blockid: blockId, }; return this.executeRequest( { url: uploadUrl, method: 'PUT', data: blockBlob, processData: false, contentType: false, }, params, ); } putBlockList(uploadUrl, blockCount) { var content = this.getBlockListContent(blockCount); var params: RequestParams = { comp: 'blocklist', }; return this.executeRequest( { url: uploadUrl, method: 'PUT', data: content, }, params, ); } getBlockListContent(blockCount) { var contentParts = [ '<?xml version="1.0" encoding="utf-8"?>', '<BlockList>', ]; for (var i = 0; i < blockCount; i++) { var blockContent = ` <Latest>${this.getBlockId(i)}</Latest>`; contentParts.push(blockContent); } contentParts.push('</BlockList>'); return contentParts.join('\n'); } getBlockId(blockIndex: number): string { var res = `${blockIndex}`; while (res.length < 10) { res = `0${res}`; } return btoa(res); } getUploadAccessUrl(blobName: string): Promise<AccessUrls | any> { return this.getAccessUrl('UploadBlob', blobName); } getBlobUrl(blobName: string): Promise<AccessUrls | any> { return this.getAccessUrl('GetBlob', blobName); } getAccessUrl( command: string, blobName?: string, blobName2?: string, ): Promise<AccessUrls | any> { var url = `${this.endpointUrl}?command=${command}`; if (blobName) { url += `&blobName=${encodeURIComponent(blobName)}`; } if (blobName2) { url += `&blobName2=${encodeURIComponent(blobName2)}`; } // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { await this.executeRequest(url).then((x) => { if (x.success) { resolve({ url1: x.accessUrl, url2: x.accessUrl2 }); } else { reject(x.error); } }); }); } executeRequest(args: any, commandParams?: RequestParams): Promise<any> { var ajaxArgs = typeof args === 'string' ? { url: args } : args; var method = ajaxArgs.method || 'GET'; var urlParts = ajaxArgs.url.split('?'); var urlPath = urlParts[0]; var restQueryString = urlParts[1]; var commandQueryString = commandParams ? this.getQueryString(commandParams) : ''; var queryString = commandQueryString || ''; if (restQueryString) { queryString += queryString ? `&${restQueryString}` : restQueryString; } ajaxArgs.url = queryString ? `${urlPath}?${queryString}` : urlPath; return fetch(ajaxArgs.url, ajaxArgs) .then((x) => { var eventArgs = { method, urlPath, queryString, }; if (this.onRequestExecuted) { this.onRequestExecuted(eventArgs); } return x; }) .then(async (x) => { if (x.status === 200) { var text = await x.text(); try { return { success: true, ...JSON.parse(text) }; } catch (ex) { return text; } } else { return { error: x.statusText }; } }); } getQueryString(params: object): string { return Object.keys(params) .map((key) => `${key}=${encodeURIComponent(params[key])}`) .join('&'); } } type FileEntry = { etag?: string; name?: string; lastModified?: Date; length?: number; }; type RequestParams = { restype?: string; comp?: string; prefix?: string; blockid?: string; }; export type AccessUrls = { url1: string; url2: string; };
// In real applications, you should not transpile code in the browser. // You can see how to create your own application with Angular and DevExtreme here: // https://js.devexpress.com/Documentation/Guide/Angular_Components/Getting_Started/Create_a_DevExtreme_Application/ window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, }, meta: { 'typescript': { 'exports': 'ts', }, 'devextreme/localization.js': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', }, map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', '@angular/core': 'npm:@angular/core@12.2.17', '@angular/platform-browser': 'npm:@angular/platform-browser@12.2.17', '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic@12.2.17', '@angular/forms': 'npm:@angular/forms@12.2.17', '@angular/common': 'npm:@angular/common@12.2.17', '@angular/compiler': 'npm:@angular/compiler@12.2.17', 'tslib': 'npm:tslib@2.3.1/tslib.js', 'rxjs': 'npm:rxjs@7.5.3/dist/bundles/rxjs.umd.js', 'rxjs/operators': 'npm:rxjs@7.5.3/dist/cjs/operators/index.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.28.0/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@22.2.6/cjs', 'devextreme/bundles/dx.all': 'npm:devextreme@22.2.6/bundles/dx.all.js', 'jszip': 'npm:jszip@3.7.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.5.20/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.1.72', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.43', 'devextreme-angular': 'npm:devextreme-angular@22.2.6', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.11', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate@7.4.11/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.4/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.4/parser-html.js', }, packages: { 'app': { main: './app.component.ts', defaultExtension: 'ts', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, 'rxjs': { defaultExtension: 'js', }, 'rxjs/operators': { defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.11/inferno/package.json', 'npm:@angular/*/package.json', 'npm:@angular/common@12.2.17/*/package.json', 'npm:rxjs@7.5.3/package.json', 'npm:rxjs@7.5.3/operators/package.json', 'npm:devextreme-angular@22.2.6/*/package.json', 'npm:devextreme-angular@22.2.6/ui/*/package.json', 'npm:devextreme-angular@22.2.6/package.json', 'npm:devexpress-diagram@2.1.72/package.json', 'npm:devexpress-gantt@4.1.43/package.json', ], }; System.config(window.config);
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/22.2.6/css/dx.light.css" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/22.2.6/css/dx-gantt.css" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/zone.js@0.13.0/dist/zone.js"></script> <script src="https://unpkg.com/reflect-metadata@0.1.13/Reflect.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script src="config.js"></script> <script> System.import("app").catch(console.error.bind(console)); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <demo-app>Loading...</demo-app> </div> </body> </html>
using DevExtreme.MVC.Demos.Models.FileManagement; using System; using System.Net.Http; using System.Web.Http; using Azure.Storage; using Azure.Storage.Sas; using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; using Azure.Storage.Blobs.Specialized; namespace DevExtreme.MVC.Demos.Controllers.ApiControllers { public class FileManagerAzureAccessApiController : ApiController { const string EmptyDirDummyBlobName = "aspxAzureEmptyFolderBlob"; const string ServiceUri = "https://{0}.blob.core.windows.net"; const long MaxBlobSize = 1048576; public FileManagerAzureAccessApiController() { AllowDownload = true; //uncomment the code below to enable file/folder management //AllowCreate = true; //AllowRemove = true; //AllowRenameOrMoveOrCopy = true; //AllowUpload = true; } bool AllowCreate { get; } bool AllowRemove { get; } bool AllowRenameOrMoveOrCopy { get; } bool AllowUpload { get; } bool AllowDownload { get; } BlobServiceClient _client; BlobServiceClient Client { get { if(_client == null) { AzureStorageAccount accountModel = AzureStorageAccount.FileManager.Value; StorageSharedKeyCredential credential = new StorageSharedKeyCredential(accountModel.AccountName, accountModel.AccessKey); _client = new BlobServiceClient(new Uri(string.Format(ServiceUri, accountModel.AccountName)), credential); } return _client; } } BlobContainerClient _container; BlobContainerClient Container { get { if(_container == null) { AzureStorageAccount accountModel = AzureStorageAccount.FileManager.Value; _container = Client.GetBlobContainerClient(accountModel.ContainerName); } return _container; } } [HttpGet] [Route("api/file-manager-azure-access", Name = "FileManagerAzureAccessApi")] public HttpResponseMessage Process(string command, string blobName = null, string blobName2 = null) { object result; try { result = ProcessCommand(command, blobName, blobName2); } catch { result = CreateErrorResult(); } return Request.CreateResponse(result); } object ProcessCommand(string command, string blobName, string blobName2) { switch(command) { case "BlobList": return GetBlobList(); case "CreateDirectory": if(!AllowCreate) return CreateErrorResult(); return CreateDirectory(blobName); case "DeleteBlob": if(!AllowRemove) return CreateErrorResult(); return DeleteBlob(blobName); case "CopyBlob": if(!AllowRenameOrMoveOrCopy) return CreateErrorResult(); return CopyBlob(blobName, blobName2); case "UploadBlob": if(!AllowUpload) return CreateErrorResult(); return UploadBlob(blobName); case "GetBlob": if(!AllowDownload) return CreateErrorResult(); return GetBlob(blobName); } return null; } object GetBlobList() { if(Container.CanGenerateSasUri) { var sasUri = Container.GenerateSasUri(BlobContainerSasPermissions.List, DateTimeOffset.UtcNow.AddHours(1)); return CreateSuccessResult(sasUri); } else { return CreateErrorResult("BlobContainerClient cannot generate SasUri"); } } object CreateDirectory(string directoryName) { string blobName = $"{directoryName}/{EmptyDirDummyBlobName}"; var blob = Container.GetBlobClient(blobName); if(blob.Exists()) { return CreateErrorResult(); } var sasUri = TryGetBlobUri(blob, BlobSasPermissions.Write); if(sasUri != null) { return CreateSuccessResult(sasUri); } else { return CreateErrorResult("BlobClient cannot generate SasUri"); } } object DeleteBlob(string blobName) { var sasUri = TryGetBlobUri(blobName, BlobSasPermissions.Delete); if(sasUri != null) { return CreateSuccessResult(sasUri); } else { return CreateErrorResult("BlobClient cannot generate SasUri"); } } object CopyBlob(string sourceBlobName, string destinationBlobName) { var sourceSasUri = TryGetBlobUri(sourceBlobName, BlobSasPermissions.Read); var destinationSasUri = TryGetBlobUri(destinationBlobName, BlobSasPermissions.Create); if(sourceSasUri != null && destinationSasUri != null) { return CreateSuccessResult(sourceSasUri, destinationSasUri); } else { return CreateErrorResult("BlobClient cannot generate SasUri"); } } object UploadBlob(string blobName) { if(blobName.EndsWith("/")) return CreateErrorResult("Invalid blob name."); var blob = Container.GetBlockBlobClient(blobName); if(blob.Exists() && blob.GetProperties().Value.ContentLength > MaxBlobSize) { return CreateErrorResult(); } var sasUri = TryGetBlobUri(blobName, BlobSasPermissions.Write); return CreateSuccessResult(sasUri); } object GetBlob(string blobName) { var headers = new BlobHttpHeaders { ContentType = "application/octet-stream" }; var blob = Container.GetBlobClient(blobName); blob.SetHttpHeaders(headers); var sasUri = TryGetBlobUri(blob, BlobSasPermissions.Read); return CreateSuccessResult(sasUri); } Uri TryGetBlobUri(string blobName, BlobSasPermissions permissions) { if(!string.IsNullOrEmpty(blobName)) { return TryGetBlobUri(Container.GetBlobClient(blobName), permissions); } else { return null; } } Uri TryGetBlobUri(BlobClient blob, BlobSasPermissions permissions) { if(blob.CanGenerateSasUri) { return blob.GenerateSasUri(permissions, DateTimeOffset.UtcNow.AddHours(1)); } else { return null; } } object CreateSuccessResult(Uri uri, Uri uri2 = null) { return new { success = true, accessUrl = uri.AbsoluteUri, accessUrl2 = uri2 != null ? uri2.AbsoluteUri : null }; } object CreateErrorResult(string error = null) { if(string.IsNullOrEmpty(error)) error = "Unspecified error."; return new { success = false, error = error }; } } }