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. On the server, configure the Shared Access Signature (SAS) to grant access to blobs in the storage.

Backend API
Copy to CodeSandBox
Apply
Reset
import React from 'react'; import FileManager, { Permissions } from 'devextreme-react/file-manager'; import CustomFileSystemProvider from 'devextreme/file_management/custom_provider'; import { LoadPanel } from 'devextreme-react/load-panel'; const endpointUrl = 'https://js.devexpress.com/Demos/Mvc/api/file-manager-azure-access'; const allowedFileExtensions = []; const loadPanelPosition = { of: '#file-manager' }; class App extends React.Component { constructor() { super(); this.state = { requests: [], loadPanelVisible: true, wrapperClassName: '' }; this.onRequestExecuted = this.onRequestExecuted.bind(this); gateway = new AzureGateway(endpointUrl, this.onRequestExecuted); azure = new AzureFileSystem(gateway); this.fileSystemProvider = new CustomFileSystemProvider({ getItems, createDirectory, renameItem, deleteItem, copyItem, moveItem, uploadFileChunk, downloadItems }); this.checkAzureStatus(); } render() { return ( <div id="wrapper" className={this.state.wrapperClassName}> <LoadPanel visible={this.state.loadPanelVisible} position={loadPanelPosition} /> <div id="widget-area"> <FileManager id="file-manager" fileSystemProvider={this.fileSystemProvider} allowedFileExtensions={allowedFileExtensions}> {/* uncomment the code below to enable file/directory management */} <Permissions create={true} copy={true} move={true} delete={true} rename={true} upload={true} download={true}> </Permissions> </FileManager> <div id="request-panel"> { this.state.requests.map((r, i) => { return <div key={i} className="request-info"> <div className="parameter-info"> <div className="parameter-name">Method:</div> <div className="parameter-value dx-theme-accent-as-text-color">{r.method}</div> </div> <div className="parameter-info"> <div className="parameter-name">Url path:</div> <div className="parameter-value dx-theme-accent-as-text-color">{r.urlPath}</div> </div> <div className="parameter-info"> <div className="parameter-name">Query string:</div> <div className="parameter-value dx-theme-accent-as-text-color">{r.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/React/Light/" target="_blank" rel="noopener noreferrer"> https://js.devexpress.com/Demos/WidgetsGallery/Demo/FileManager/AzureClientBinding/React/Light/</a> to see the demo online. </div> </div> ); } checkAzureStatus() { fetch('https://js.devexpress.com/Demos/Mvc/api/file-manager-azure-status?widgetType=fileManager') .then(response => response.json()) .then(result => { const className = result.active ? 'show-widget' : 'show-message'; this.setState({ wrapperClassName: className, loadPanelVisible: false }); }); } onRequestExecuted({ method, urlPath, queryString }) { const request = { method, urlPath, queryString }; this.setState({ requests: [request, ...this.state.requests] }); } } 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).done(accessUrl => { uploadInfo.customData.accessUrl = accessUrl; }); } 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; export default App;
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.js'; ReactDOM.render( <App />, document.getElementById('app') );
<!DOCTYPE html> <html> <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/21.1.5/css/dx.common.css" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/21.1.5/css/dx.light.css" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/21.1.5/css/dx-gantt.min.css" /> <link rel="stylesheet" type="text/css" href="styles.css" /> <script src="../azure-file-system.js"></script> <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script type="text/javascript" src="config.js"></script> <script type="text/javascript"> System.import('./index.js'); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"></div> </div> </body> </html>
#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; }
window.config = { transpiler: 'plugin-babel', meta: { 'devextreme/localization.js': { "esModule": true }, }, paths: { 'npm:': 'https://unpkg.com/' }, defaultExtension: 'js', map: { 'react': 'npm:react@17.0.2/umd/react.development.js', 'react-dom': 'npm:react-dom@17.0.2/umd/react-dom.development.js', 'prop-types': 'npm:prop-types@15.7.2/prop-types.js', 'rrule': 'npm:rrule@2.6.6/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@21.1.6-build-21215-0313/cjs', 'devextreme-react': 'npm:devextreme-react@21.1.5', 'jszip': 'npm:jszip@3.7.0/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.3.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.1.25/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@3.0.4/dist/dx-gantt.js', '@devextreme/vdom': 'npm:@devextreme/vdom@1.2.2', 'inferno': 'npm:inferno@7.4.8/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat@7.4.8/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.8/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom@1.0.7/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate@7.4.8/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode@7.4.8/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class@7.4.8/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras@7.4.8/dist/inferno-extras.min.js', // SystemJS plugins 'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js', 'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js' }, packages: { 'devextreme': { defaultExtension: 'js' }, '@devextreme/vdom': { defaultExtension: 'js' }, 'devextreme-react': { main: 'index.js' }, 'devextreme/events/utils': { main: 'index' }, 'devextreme/events': { main: 'index' }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js' } }, packageConfigPaths: [ "npm:@devextreme/*/package.json", ], babelOptions: { sourceMaps: false, stage0: true, react: true } }; System.config(window.config);
using DevExtreme.MVC.Demos.Models.FileManagement; using Microsoft.Azure.Storage; using Microsoft.Azure.Storage.Auth; using Microsoft.Azure.Storage.Blob; using System; using System.Net; using System.Net.Http; using System.Web.Http; namespace DevExtreme.MVC.Demos.Controllers.ApiControllers { public class FileManagerAzureAccessApiController : ApiController { const string EmptyDirDummyBlobName = "aspxAzureEmptyFolderBlob"; 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; } CloudBlobClient _client; CloudBlobClient Client { get { if(this._client == null) { AzureStorageAccount accountModel = AzureStorageAccount.FileManager.Value; var credentials = new StorageCredentials(accountModel.AccountName, accountModel.AccessKey); var account = new CloudStorageAccount(credentials, true); this._client = account.CreateCloudBlobClient(); } return this._client; } } CloudBlobContainer _container; CloudBlobContainer Container { get { if(this._container == null) { AzureStorageAccount accountModel = AzureStorageAccount.FileManager.Value; this._container = Client.GetContainerReference(accountModel.ContainerName); } return this._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() { var policy = CreateSharedAccessBlobPolicy(SharedAccessBlobPermissions.List); string url = Container.Uri + Container.GetSharedAccessSignature(policy, null, SharedAccessProtocol.HttpsOnly, null); return CreateSuccessResult(url); } object CreateDirectory(string directoryName) { string blobName = $"{directoryName}/{EmptyDirDummyBlobName}"; CloudBlockBlob blob = Container.GetBlockBlobReference(blobName); if(blob.Exists()) { if(blob.Properties.Length > 0) { blob.Delete(); } return CreateErrorResult(); } string url = GetSharedAccessSignature(blob, SharedAccessBlobPermissions.Write); return CreateSuccessResult(url); } object DeleteBlob(string blobName) { string url = GetSharedAccessSignature(blobName, SharedAccessBlobPermissions.Delete); return CreateSuccessResult(url); } object CopyBlob(string sourceBlobName, string destinationBlobName) { string sourceUrl = GetSharedAccessSignature(sourceBlobName, SharedAccessBlobPermissions.Read); string destinationUrl = GetSharedAccessSignature(destinationBlobName, SharedAccessBlobPermissions.Create); return CreateSuccessResult(sourceUrl, destinationUrl); } object UploadBlob(string blobName) { if(blobName.EndsWith("/")) return CreateErrorResult("Invalid blob name."); CloudBlockBlob blob = Container.GetBlockBlobReference(blobName); if(blob.Exists() && blob.Properties.Length > MaxBlobSize) { blob.Delete(); return CreateErrorResult(); } string url = GetSharedAccessSignature(blob, SharedAccessBlobPermissions.Write); return CreateSuccessResult(url); } object GetBlob(string blobName) { var headers = new SharedAccessBlobHeaders { ContentType = "application/octet-stream" }; string url = GetSharedAccessSignature(blobName, SharedAccessBlobPermissions.Read, headers); return CreateSuccessResult(url); } string GetSharedAccessSignature(string blobName, SharedAccessBlobPermissions permissions, SharedAccessBlobHeaders headers = null) { CloudBlockBlob blob = Container.GetBlockBlobReference(blobName); return GetSharedAccessSignature(blob, permissions, headers); } string GetSharedAccessSignature(CloudBlockBlob blob, SharedAccessBlobPermissions permissions, SharedAccessBlobHeaders headers = null) { SharedAccessBlobPolicy policy = CreateSharedAccessBlobPolicy(permissions); return blob.Uri + blob.GetSharedAccessSignature(policy, headers, null, SharedAccessProtocol.HttpsOnly, null); } SharedAccessBlobPolicy CreateSharedAccessBlobPolicy(SharedAccessBlobPermissions permissions) { return new SharedAccessBlobPolicy { SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(1), Permissions = permissions }; } object CreateSuccessResult(string url, string url2 = null) { return new { success = true, accessUrl = url, accessUrl2 = url2 }; } object CreateErrorResult(string error = null) { if(string.IsNullOrEmpty(error)) error = "Unspecified error."; return new { success = false, error = error }; } } }