Your search did not match any results.
File Uploader

Direct Upload to Azure

The FileUploader component supports direct-upload to blob storages. This demo illustrates how to configure the uploadChunk property to upload a large file directly to Azure Blob Storage without using a user's web server. 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).

To implement file upload logic, use the uploadChunk property to specify how to process a connection request to the storage.

Backend API
Copy to CodeSandBox
Apply
Reset
<template> <div id="wrapper" :class="wrapperClassName" > <DxLoadPanel :position="loadPanelPosition" v-model:visible="loadPanelVisible" /> <div id="widget-area"> <DxFileUploader id="file-uploader" :chunk-size="200000" :max-file-size="1048576" :upload-chunk="uploadChunk" /> <div id="request-panel"> <div class="request-info" v-for="(request, index) in requests" :key="index" > <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/FileUploader/AzureDirectUploading/Vue/Light/" target="_blank" > https://js.devexpress.com/Demos/WidgetsGallery/Demo/FileUploader/AzureDirectUploading/Vue/Light/ </a> to see the demo online. </div> </div> </template> <script> import { DxFileUploader } from 'devextreme-vue/file-uploader'; import { DxLoadPanel } from 'devextreme-vue/load-panel'; import { AzureGateway } from './azure.file.system.js'; // eslint-disable-line import/no-unresolved const endpointUrl = 'https://js.devexpress.com/Demos/Mvc/api/file-manager-azure-access'; export default { components: { DxFileUploader, DxLoadPanel, }, data() { return { loadPanelPosition: { of: '#file-uploader' }, loadPanelVisible: true, wrapperClassName: '', requests: [], }; }, created() { const onRequestExecuted = ({ method, urlPath, queryString }) => { const request = { method, urlPath, queryString }; this.requests.unshift(request); }; gateway = new AzureGateway(endpointUrl, onRequestExecuted); fetch('https://js.devexpress.com/Demos/Mvc/api/file-manager-azure-status?widgetType=fileManager') .then((response) => response.json()) .then((result) => { this.wrapperClassName = result.active ? 'show-widget' : 'show-message'; this.loadPanelVisible = false; }); }, methods: { uploadChunk(file, uploadInfo) { let promise = null; if (uploadInfo.chunkIndex === 0) { // eslint-disable-next-line spellcheck/spell-checker promise = gateway.getUploadAccessUrl(file.name).then((accessUrls) => { // eslint-disable-next-line spellcheck/spell-checker 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; }, }, }; let gateway = null; </script> <style> #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; } </style>
export class AzureFileSystem { 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) => { 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 { constructor(endpointUrl, onRequestExecuted) { this.endpointUrl = endpointUrl; this.onRequestExecuted = onRequestExecuted; } getBlobList(prefix) { return this.getAccessUrl('BlobList') .then((accessUrls) => this.executeBlobListRequest(accessUrls.url1, prefix)) .then((xml) => this.parseEntryListResult(xml)); } parseEntryListResult(xmlString) { var xml = new DOMParser().parseFromString(xmlString, 'text/xml'); return Array.from(xml.querySelectorAll('Blob')).map(this.parseEntry); } parseEntry(xmlEntry) { var entry = {}; 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 = { restype: 'container', comp: 'list', }; if (prefix) { params.prefix = prefix; } return this.executeRequest(accessUrl, params); } createDirectoryBlob(name) { return this.getAccessUrl('CreateDirectory', name).then((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) => this.executeRequest({ url: accessUrls.url1, method: 'DELETE', })); } copyBlob(sourceName, destinationName) { return this.getAccessUrl('CopyBlob', sourceName, destinationName).then((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 = { 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 = { 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) { var res = `${blockIndex}`; while (res.length < 10) { res = `0${res}`; } return btoa(res); } getUploadAccessUrl(blobName) { return this.getAccessUrl('UploadBlob', blobName); } getBlobUrl(blobName) { return this.getAccessUrl('GetBlob', blobName); } getAccessUrl(command, blobName, blobName2) { 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, commandParams) { 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 Object.assign({ success: true }, JSON.parse(text)); } catch (ex) { return text; } } else { return { error: x.statusText }; } }); } getQueryString(params) { return Object.keys(params) .map((key) => `${key}=${encodeURIComponent(params[key])}`) .join('&'); } }
import { createApp } from 'vue'; import App from './App.vue'; createApp(App).mount('#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/22.2.3/css/dx.light.css" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/22.2.3/css/dx-gantt.css" /> <script src="https://unpkg.com/core-js@2.6.12/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>
window.config = { transpiler: 'plugin-babel', meta: { '*.vue': { loader: 'vue-loader', }, 'devextreme/localization.js': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', }, map: { 'vue': 'npm:vue@3.2.41/dist/vue.esm-browser.js', 'vue-loader': 'npm:dx-systemjs-vue-browser@1.0.15/index.js', 'mitt': 'npm:mitt/dist/mitt.umd.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.3/cjs', 'devextreme-vue': 'npm:devextreme-vue@22.2.3', 'jszip': 'npm:jszip@3.7.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.5.18/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.1.65/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.37/dist/dx-gantt.js', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.11', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat@7.4.11/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@7.4.11/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class@7.4.11/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras@7.4.11/dist/inferno-extras.min.js', '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', // Prettier 'prettier/standalone': 'npm:prettier@2.7.1/standalone.js', 'prettier/parser-html': 'npm:prettier@2.7.1/parser-html.js', }, packages: { 'devextreme-vue': { main: 'index.js', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.11/inferno/package.json', ], babelOptions: { sourceMaps: false, stage0: true, }, }; System.config(window.config);
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.Specialized; namespace DevExtreme.MVC.Demos.Controllers.ApiControllers { public class FileUploaderAzureAccessApiController : ApiController { const long MaxBlobSize = 1048576; const string ServiceUri = "https://{0}.blob.core.windows.net"; BlobServiceClient _client; BlobServiceClient Client { get { if(_client == null) { AzureStorageAccount accountModel = AzureStorageAccount.FileUploader.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.FileUploader.Value; _container = Client.GetBlobContainerClient(accountModel.ContainerName); } return _container; } } [HttpGet] [Route("api/file-uploader-azure-access", Name = "FileUploaderAzureAccessApi")] public object Process(string command, string blobName) { try { return UploadBlob(blobName); } catch { return CreateErrorResult(); } } object UploadBlob(string blobName) { if(blobName.Contains("/")) return CreateErrorResult("Invalid blob name."); string prefix = Guid.NewGuid().ToString("N"); string fullBlobName = $"{prefix}_{blobName}"; var blob = Container.GetBlockBlobClient(fullBlobName); if(blob.Exists() && blob.GetProperties().Value.ContentLength > MaxBlobSize) { return CreateErrorResult(); } if(blob.CanGenerateSasUri) { var sasUri = blob.GenerateSasUri(BlobSasPermissions.Write, DateTimeOffset.UtcNow.AddHours(1)); return CreateSuccessResult(sasUri.AbsoluteUri); } else { return CreateErrorResult("BlobClient cannot generate SasUri"); } } 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 }; } } }