<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel xmlns:media="http://search.yahoo.com/mrss/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <title>ABP.IO Stories</title>
    <link>https://abp.io/community/articles</link>
    <description>A hub for ABP Framework, .NET, and software development. Access articles, tutorials, news, and contribute to the ABP community.</description>
    <lastBuildDate>Wed, 14 Oct 2026 19:02:43 Z</lastBuildDate>
    <generator>Community - ABP.IO</generator>
    <image>
      <url>https://abp.io/assets/favicon.ico/favicon-32x32.png</url>
      <title>ABP.IO Stories</title>
      <link>https://abp.io/community/articles</link>
    </image>
    <a10:link rel="self" type="application/rss+xml" title="self" href="https://abp.io/community/rss?member=cotur" />
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/using-devextreme-components-with-the-abp-framework-zb8z7yqv</guid>
      <link>https://abp.io/community/posts/using-devextreme-components-with-the-abp-framework-zb8z7yqv</link>
      <a10:author>
        <a10:name>cotur</a10:name>
        <a10:uri>https://abp.io/community/members/cotur</a10:uri>
      </a10:author>
      <category>DevExpress</category>
      <category>DevExtreme</category>
      <title>Using DevExtreme Components With the ABP Framework</title>
      <description>Hi, in this step by step article, I will show you how to integrate DevExtreme components into ABP Framework-based applications.

</description>
      <pubDate>Fri, 28 Aug 2020 08:18:37 Z</pubDate>
      <a10:updated>2026-10-14T12:55:49Z</a10:updated>
      <content:encoded><![CDATA[<h1>Using DevExtreme Components With the ABP Framework</h1>
<p>Hi, in this step by step article, I will show you how to integrate <a href="https://js.devexpress.com/">DevExtreme</a> components into ABP Framework-based applications.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/both-example-result.png" alt="both-example-result" /></p>
<p><em>(A screenshot from the example application developed in this article)</em></p>
<h2>Create the Project</h2>
<p>ABP Framework offers startup templates to get into the business faster. We can download a new startup template using <a href="https://docs.abp.io/en/abp/latest/CLI">ABP CLI</a>:</p>
<pre><code class="language-bash">abp new DevExtremeSample
</code></pre>
<p>After the download is finished, open the solution in the Visual Studio (or your favorite IDE):</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/initial-project.png" alt="initial-project" /></p>
<p>Run the <code>DevExtremeSample.DbMigrator</code> application to create the database and seed initial data (which creates the admin user, admin role, related permissions, etc). Then we can run the <code>DevExtremeSample.Web</code> project to see our application working.</p>
<blockquote>
<p><em>Default admin username is <strong>admin</strong> and password is <strong>1q2w3E*</strong></em></p>
</blockquote>
<h2>Install DevExtreme</h2>
<p>You can follow <a href="https://js.devexpress.com/Documentation/17_1/Guide/ASP.NET_MVC_Controls/Prerequisites_and_Installation/">this documentation</a> to install DevExpress packages into your computer.</p>
<blockquote>
<p>Don't forget to add <em>&quot;DevExpress NuGet Feed&quot;</em> to your <strong>Nuget Package Sources</strong>.</p>
</blockquote>
<h3>Adding DevExtreme NuGet Packages</h3>
<p>Add the <code>DevExtreme.AspNet.Core</code> NuGet package to the <code>DevExtremeSample.Application.Contracts</code> project.</p>
<pre><code>Install-Package DevExtreme.AspNet.Core
</code></pre>
<p>Add the <code>DevExtreme.AspNet.Data</code> package to your <code>DevExtremeSample.Web</code> project.</p>
<pre><code>Install-Package DevExtreme.AspNet.Data
</code></pre>
<h3>Adding DevExtreme NPM Dependencies</h3>
<p>Open your <code>DevExtremeSample.Web</code> project folder with a command line and add <code>devextreme</code> and <code>devextreme-aspnet-data</code> NPM packages:</p>
<pre><code class="language-bash">npm install devextreme
</code></pre>
<pre><code class="language-bash">npm install devextreme-aspnet-data
</code></pre>
<h3>Adding Resource Mappings</h3>
<p>The <code>devextreme</code> and <code>devextreme-aspnet-data</code> NPM packages are saved under <code>node_modules</code> folder. We need to move the needed files in our <code>wwwroot/libs</code> folder to use them in our web project. We can do it using the ABP <a href="https://docs.abp.io/en/abp/latest/UI/AspNetCore/Client-Side-Package-Management">client side resource mapping</a> system.</p>
<p>Open the <code>abp.resourcemapping.js</code> file in your <code>DevExtremeSample.Web</code> project and add the following definitions to inside <code>mappings</code> object.</p>
<pre><code class="language-json">&quot;@node_modules/devextreme/dist/**/*&quot;: &quot;@libs/devextreme/&quot;,
&quot;@node_modules/devextreme-aspnet-data/js/dx.aspnet.data.js&quot;: &quot;@libs/devextreme/js/&quot;
</code></pre>
<p>The final <code>abp.resourcemapping.js</code> file should look like below:</p>
<pre><code>module.exports = {
  aliases: {},
  mappings: {
    &quot;@node_modules/devextreme/dist/**/*&quot;: &quot;@libs/devextreme/&quot;,
    &quot;@node_modules/devextreme-aspnet-data/js/dx.aspnet.data.js&quot;: &quot;@libs/devextreme/&quot;
  },
};
</code></pre>
<p>Open your <code>DevExtremeSample.Web</code> project folder with a command line and run the <code>abp install-libs</code> command. This command will copy the needed library files into the <code>/wwwroot/libs/devextreme/</code> folder.</p>
<pre><code class="language-bash">abp install-libs
</code></pre>
<p>You can see <code>devextreme</code> folder inside the <code>wwwroot/libs</code>:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/wwwroot-lib.png" alt="wwwroot-lib" /></p>
<h3>Adding DevExtremeStyleContributor</h3>
<p>We will add DevExtreme CSS files to the global bundle by creating a <a href="https://docs.abp.io/en/abp/latest/UI/AspNetCore/Bundling-Minification">bundle contributor</a>.</p>
<p>Create a <code>Bundling</code> folder in the <code>DevExtremeSample.Web</code> project and a <code>DevExtremeStyleContributor.cs</code> file with the following content:</p>
<pre><code class="language-csharp">using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;

namespace DevExtremeSample.Web.Bundling
{
    public class DevExtremeStyleContributor : BundleContributor
    {
        public override void ConfigureBundle(BundleConfigurationContext context)
        {
            context.Files.AddIfNotContains(&quot;/libs/devextreme/css/dx.common.css&quot;);
            context.Files.AddIfNotContains(&quot;/libs/devextreme/css/dx.light.css&quot;);
        }
    }
}
</code></pre>
<blockquote>
<p>You can choose another theme than the light theme. Check the <code>/libs/devextreme/css/</code> folder and the DevExtreme documentation for other themes.</p>
</blockquote>
<p>Open your <code>DevExtremeSampleWebModule.cs</code> file in your <code>DevExtremeSample.Web</code> project and add following code into the <code>ConfigureServices</code> method:</p>
<pre><code class="language-csharp">Configure&lt;AbpBundlingOptions&gt;(options =&gt;
{
    options
        .StyleBundles
        .Get(StandardBundles.Styles.Global)
        .AddContributors(typeof(DevExtremeStyleContributor));
});
</code></pre>
<h3>Adding DevExtremeScriptContributor</h3>
<p>We can not add DevExtreme js packages to Global Script Bundles, just like done for the CSS files. Because DevExtreme requires to add its JavaScript files into the <code>&lt;head&gt;</code> section of the HTML document, while ABP Framework adds all JavaScript files to the end of the <code>&lt;body&gt;</code> (as a best practice).</p>
<p>Fortunately, ABP Framework has a <a href="https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface#layout-hooks">layout hook system</a> that allows you to add any code into some specific positions in the HTML document. All you need to do is to create a <code>ViewComponent</code> and configure the layout hooks.</p>
<p>Let's begin by creating a <code>DevExtremeScriptContributor.cs</code> file in the <code>Bundling</code> folder by copying the following code inside it:</p>
<pre><code class="language-csharp">using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery;
using Volo.Abp.Modularity;

namespace DevExtremeSample.Web.Bundling
{
    [DependsOn(
        typeof(JQueryScriptContributor)
        )]
    public class DevExtremeScriptContributor : BundleContributor
    {
        public override void ConfigureBundle(BundleConfigurationContext context)
        {
            context.Files.AddIfNotContains(&quot;/libs/devextreme/js/dx.all.js&quot;);
            context.Files.AddIfNotContains(&quot;/libs/devextreme/js/dx.aspnet.mvc.js&quot;);
            context.Files.AddIfNotContains(&quot;/libs/devextreme/js/dx.aspnet.data.js&quot;);
        }
    }
}
</code></pre>
<p>As you see, the <code>DevExtremeScriptContributor</code> depends on <code>JQueryScriptContributor</code> which adds JQuery related files before the DevExpress packages (see the <a href="https://docs.abp.io/en/abp/latest/UI/AspNetCore/Bundling-Minification">bundling system</a> for details).</p>
<h4>Create DevExtremeJsViewComponent</h4>
<p>Create a new view component, named <code>DevExtremeJsViewComponent</code> inside the <code>/Components/DevExtremeJs</code> folder of the Web project, by following the steps below:</p>
<ol>
<li>Create a <code>DevExtremeJsViewComponent</code> class inside the <code>/Components/DevExtremeJs</code> (create the folders first):</li>
</ol>
<pre><code class="language-csharp">using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;

namespace DevExtremeSample.Web.Components.DevExtremeJs
{
    public class DevExtremeJsViewComponent : AbpViewComponent
    {
        public IViewComponentResult Invoke()
        {
            return View(&quot;/Components/DevExtremeJs/Default.cshtml&quot;);
        }
    }
}
</code></pre>
<ol start="2">
<li>Create <code>Default.cshtml</code> file in the same folder with the following content:</li>
</ol>
<pre><code class="language-csharp">@using DevExtremeSample.Web.Bundling
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling

&lt;abp-script type=&quot;typeof(DevExtremeScriptContributor)&quot; /&gt;
</code></pre>
<p>Your final Web project should be like the following:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/devextreme-js.png" alt="devextreme-js" /></p>
<ol start="3">
<li>Now, we can add this view component to <code>&lt;head&gt;</code> section by using the layout hooks.</li>
</ol>
<p>Open your <code>DevExtremeSampleWebModule.cs</code> file in your <code>DevExtremeSample.Web</code> project and add following code into the <code>ConfigureServices</code> method:</p>
<pre><code class="language-csharp">Configure&lt;AbpLayoutHookOptions&gt;(options =&gt;
{
    options.Add(
        LayoutHooks.Head.Last, //The hook name
        typeof(DevExtremeJsViewComponent) //The component to add
    );
});
</code></pre>
<h4>Known Issue: Uncaught TypeError: MutationObserver.observe: Argument 1 is not an object.</h4>
<blockquote>
<p>This issue does exist in the ABP Framework v3.0 and earlier versions. If you are using ABP Framework v3.1 or a later version, you can skip this section.</p>
</blockquote>
<p>When you run your <code>*.Web</code> project, you will see an exception (<code>Uncaught TypeError: MutationObserver.observe: Argument 1 is not an object.</code>) at your console.</p>
<p>To fix that issue, download this file <a href="https://github.com/abpframework/abp/blob/dev/npm/packs/jquery/src/abp.jquery.js">abp.jquery.js</a> and replace with the <code>wwwroot/libs/abp/jquery/abp.jquery.js</code> file of your Web project.</p>
<h3>Result</h3>
<p>The installation step was done. You can use any DevExtreme component in your application.</p>
<p>Example: A button and a progress bar component:</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/devexp-result.gif" alt="devexp-result" /></p>
<p>This example has been created by following <a href="https://js.devexpress.com/Demos/WidgetsGallery/Demo/ProgressBar/Overview/NetCore/Light/">this documentation</a>.</p>
<h2>The Sample Application</h2>
<p>We have created a sample application with <a href="https://demos.devexpress.com/ASPNetCore/Demo/TreeList/Overview/">Tree List</a> and <a href="https://demos.devexpress.com/ASPNetCore/Demo/DataGrid/Overview/">Data Grid</a> examples.</p>
<h3>The Source Code</h3>
<p>You can download the source code from <a href="https://github.com/abpframework/abp-samples/tree/master/DevExtreme-Mvc">here</a>.</p>
<h3>Data Grid</h3>
<p>You can see the full working example of <a href="https://demos.devexpress.com/ASPNetCore/Demo/DataGrid/Overview/">Data Grid</a>.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/data-grid-final.png" alt="data-grid-final" /></p>
<p>The related files for this example are highlighted at the following screenshots.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/data-grid-app-contract.png" alt="data-grid-app-contract" /></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/data-grid-application.png" alt="data-grid-application" /></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/data-grid-web.png" alt="data-grid-web" /></p>
<h3>Tree List</h3>
<p>You can see the full working example of <a href="https://demos.devexpress.com/ASPNetCore/Demo/TreeList/Overview/">Tree List</a>.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/tree-list-final.png" alt="tree-list-final" /></p>
<p>The related files for this example are highlighted at the following screenshots.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/tree-list-app-contract.png" alt="tree-list-app-contract" /></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/tree-list-application.png" alt="tree-list-application" /></p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-08-18-Using-DevExtreme-Components-With-The-ABP-Framework/tree-list-web.png" alt="tree-list-web" /></p>
<h3>Additional Notes</h3>
<h4>Data Storage</h4>
<p>I've used an in-memory list to store data for this example, instead of a real database. Because it is not related to DevExpress usage. There is a <code>SampleDataService.cs</code> file in <code>Data</code> folder at <code>.Application.Contracts</code> project. All the data is stored here.</p>
<h4>JSON Serialization</h4>
<p>You can see some <code>JsonProperty</code> attributes on the DTO properties. I use these attributes because DevExtreme example expects <code>PascalCase</code> property names in the serialized JSON that is sent to the client. But ABP Framework &amp; ASP.NET Core conventionally uses <code>camelCase</code> property names on JSON serialization. Adding these <code>JsonProperty</code> attributes ensures that the related properties are serialized as <code>PascalCase</code>.</p>
<h4>DevExtreme Components vs Application Service Methods</h4>
<p>ABP Framework conventionally converts application services to API Controllers. For example, see the application service below:</p>
<pre><code class="language-csharp">public class OrderAppService : DevExtremeSampleAppService, IOrderAppService
{
    public async Task&lt;LoadResult&gt; GetOrdersAsync(DataSourceLoadOptions loadOptions)
    {
        ...
    }

    public async Task&lt;Order&gt; InsertOrder(string values)
    {
        ...
    }
    ...
}
</code></pre>
<p>You can use these service methods for your DevExtreme components as shown below:</p>
<pre><code class="language-csharp">Html.DevExtreme().DataGrid&lt;Order&gt;()
            .DataSource(d =&gt; d.Mvc()
                .Controller(&quot;Order&quot;) // Application Service Name without 'AppService'
                .LoadAction(&quot;GetOrders&quot;) // Method Name without 'Async'
                .InsertAction(&quot;InsertOrder&quot;)
                .UpdateAction(&quot;UpdateOrder&quot;)
                .DeleteAction(&quot;DeleteOrder&quot;)
                .Key(&quot;OrderID&quot;)
            )
</code></pre>
<h2>Conclusion</h2>
<p>In this article, I've explained how to use <a href="https://js.devexpress.com/">DevExtreme</a> components in your application. ABP Framework is designed so that it can work with any UI library/framework.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/8ab3561f-7b05-ca12-c246-39f746527f30" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/8ab3561f-7b05-ca12-c246-39f746527f30" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/file-uploaddownload-with-blob-storage-system-in-asp.net-core-abp-framework-d01cbe12</guid>
      <link>https://abp.io/community/posts/file-uploaddownload-with-blob-storage-system-in-asp.net-core-abp-framework-d01cbe12</link>
      <a10:author>
        <a10:name>cotur</a10:name>
        <a10:uri>https://abp.io/community/members/cotur</a10:uri>
      </a10:author>
      <category>blob-storing</category>
      <category>upload-file</category>
      <title>File Upload/Download with BLOB Storage System in ASP.NET Core &amp; ABP Framework</title>
      <description>This step-by-step article describes how to upload a file to a Web server and also download by client with using ASP.NET Core &amp; ABP Framework.</description>
      <pubDate>Tue, 21 Jul 2020 15:03:34 Z</pubDate>
      <a10:updated>2026-10-14T09:28:02Z</a10:updated>
      <content:encoded><![CDATA[<h1>File Upload/Download with BLOB Storage System in ASP.NET Core &amp; ABP Framework</h1>
<h2>Introduction</h2>
<p>This step-by-step article describes how to upload a file to a Web server and also download by client with using ASP.NET Core &amp; ABP Framework. By following this article, you will create a web project and its related code to upload and download files.</p>
<p>Before the creating application, we need to know some fundamentals.</p>
<h2>BLOB Storing</h2>
<p>It is typical to <strong>store file contents</strong> in an application and read these file contents on need. Not only files, but you may also need to save various types of <strong>large binary objects</strong>, a.k.a. <a href="https://en.wikipedia.org/wiki/Binary_large_object">BLOB</a>s, into a <strong>storage</strong>. For example, you may want to save user profile pictures.</p>
<p>A BLOB is a typically <strong>byte array</strong>. There are various places to store a BLOB item; storing in the local file system, in a shared database or on the <a href="https://azure.microsoft.com/en-us/services/storage/blobs/">Azure BLOB storage</a> can be options.</p>
<p>The ABP Framework provides an abstraction to work with BLOBs and provides some pre-built storage providers that you can easily integrate to. Having such an abstraction has some benefits;</p>
<ul>
<li>You can <strong>easily integrate</strong> to your favorite BLOB storage provides with a few lines of configuration.</li>
<li>You can then <strong>easily change</strong> your BLOB storage without changing your application code.</li>
<li>If you want to create <strong>reusable application modules</strong>, you don't need to make assumption about how the BLOBs are stored.</li>
</ul>
<p>ABP BLOB Storage system is also compatible to other ABP Framework features like <a href="https://docs.abp.io/en/abp/latest/Multi-Tenancy">multi-tenancy</a>.</p>
<p>To get more information about ABP BLOB Storing system, please check this <a href="https://docs.abp.io/en/abp/latest/Blob-Storing">documentation</a>.</p>
<h2>Preparing the Project</h2>
<h3>Startup template and the initial run</h3>
<p>Abp Framework offers startup templates to get into the business faster. We can download a new startup template using Abp CLI:</p>
<p><code>abp new FileActionsDemo -m none</code></p>
<p>After the download is finished, we run <code>FileActionsDemo.DbMigrator</code> project to create the database and seed initial data (admin user, role, etc). Then we run <code>FileActionsDemo.Web</code> to see our application working.</p>
<blockquote>
<p><em>Default admin username is <strong>admin</strong> and password is <strong>1q2w3E*</strong></em></p>
</blockquote>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/initial-project.png" alt="initial-project" /></p>
<h3>Adding Blob Storing Module</h3>
<p>For this article, we use <a href="https://docs.abp.io/en/abp/latest/Blob-Storing-Database">Blob Storing Database Provider</a>.</p>
<p>You can use <a href="https://docs.abp.io/en/abp/latest/Blob-Storing-Azure">Azure</a> or <a href="https://docs.abp.io/en/abp/latest/Blob-Storing-File-System">File System</a> providers also.</p>
<p>Open a command prompt (terminal) in the folder containing your solution (.sln) file and run the following command:</p>
<p><code>abp add-module Volo.Abp.BlobStoring.Database</code></p>
<p>This action will add the module dependencies and also module migration. After this action, run <code>FileActionsDemo.DbMigrator</code> to update the database.</p>
<h3>Setting up Blob Storage</h3>
<p>BLOB Storage system works with <code>Containers</code>. Before the using blob storage, we need to create our blob container.</p>
<p>Create a class that name <code>MyFileContainer</code> at the <code>FileActionsDemo.Domain</code> project.</p>
<pre><code class="language-csharp">using Volo.Abp.BlobStoring;

namespace FileActionsDemo
{
    [BlobContainerName(&quot;my-file-container&quot;)]
    public class MyFileContainer
    {

    }
}
</code></pre>
<p>That's all, we can start to use BLOB storing in our application.</p>
<h2>Creating Application Layer</h2>
<p>Before the creating Application Service, we need to create some <a href="https://docs.abp.io/en/abp/latest/Data-Transfer-Objects">DTO</a>s that used by Application Service.</p>
<p>Create following DTOs in <code>FileActionsDemo.Application.Contracts</code> project.</p>
<ul>
<li><p><code>BlobDto.cs</code></p>
<pre><code class="language-csharp">namespace FileActionsDemo
{
    public class BlobDto
    {
        public byte[] Content { get; set; }

        public string Name { get; set; }
    }
}
</code></pre>
</li>
<li><p><code>GetBlobRequestDto.cs</code></p>
<pre><code class="language-csharp">using System.ComponentModel.DataAnnotations;

namespace FileActionsDemo
{
    public class GetBlobRequestDto
    {
        [Required]
        public string Name { get; set; }
    }
}
</code></pre>
</li>
<li><p><code>SaveBlobInputDto.cs</code></p>
<pre><code class="language-csharp">using System.ComponentModel.DataAnnotations;

namespace FileActionsDemo
{
    public class SaveBlobInputDto
    {
        public byte[] Content { get; set; }

        [Required]
        public string Name { get; set; }
    }
}
</code></pre>
</li>
</ul>
<p>Create <code>IFileAppService.cs</code> interface at the same place with DTOs.</p>
<ul>
<li><p><code>IFileAppService</code></p>
<pre><code class="language-csharp">using System.Threading.Tasks;
using Volo.Abp.Application.Services;

namespace FileActionsDemo
{
    public interface IFileAppService : IApplicationService
    {
        Task SaveBlobAsync(SaveBlobInputDto input);

        Task&lt;BlobDto&gt; GetBlobAsync(GetBlobRequestDto input);
    }
}
</code></pre>
</li>
</ul>
<p>After creating DTOs and interface, <code>FileActionsDemo.Application.Contracts</code> project should be like as following image.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/application-contracts-project.png" alt="application-contracts-project" /></p>
<p>Then we can create our Application Service.</p>
<p>Create <code>FileAppService.cs</code> in <code>FileActionsDemo.Application</code> project.</p>
<pre><code class="language-csharp">using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.BlobStoring;

namespace FileActionsDemo
{
    public class FileAppService : ApplicationService, IFileAppService
    {
        private readonly IBlobContainer&lt;MyFileContainer&gt; _fileContainer;

        public FileAppService(IBlobContainer&lt;MyFileContainer&gt; fileContainer)
        {
            _fileContainer = fileContainer;
        }

        public async Task SaveBlobAsync(SaveBlobInputDto input)
        {
            await _fileContainer.SaveAsync(input.Name, input.Content, true);
        }

        public async Task&lt;BlobDto&gt; GetBlobAsync(GetBlobRequestDto input)
        {
            var blob = await _fileContainer.GetAllBytesAsync(input.Name);

            return new BlobDto
            {
                Name = input.Name,
                Content = blob
            };
        }
    }
}
</code></pre>
<p>As you see in previous code block, we inject <code>IBlobContainer&lt;MyFileContainer&gt;</code> to our app service. It will handle all blob actions for us.</p>
<ul>
<li><p><code>SaveBlobAsync</code> method uses <code>SaveAsync</code> of <code>IBlobContainer&lt;MyFileContainer&gt;</code> to save the given blob to storage, this is a simple example so we don't check is there any file exist with same name. We sent blob name, blob content and <code>true</code> for <code>overrideExisting</code> parameter.</p>
</li>
<li><p><code>GetBlobAsync</code> method is uses <code>GetAllBytesAsync</code> of <code>IBlobContainer&lt;MyFileContainer&gt;</code> to get blob content by name.</p>
</li>
</ul>
<p>We finished the application layer for this project. After that we will create a <code>Controller</code> for API and <code>Razor Page</code> for UI.</p>
<h2>Creating Controller</h2>
<p>Create <code>FileController.cs</code> in your <code>FileActionsDemo.HttpApi</code> project.</p>
<pre><code class="language-csharp">using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;

namespace FileActionsDemo
{
    public class FileController : AbpController
    {
        private readonly IFileAppService _fileAppService;

        public FileController(IFileAppService fileAppService)
        {
            _fileAppService = fileAppService;
        }

        [HttpGet]
        [Route(&quot;download/{fileName}&quot;)]
        public async Task&lt;IActionResult&gt; DownloadAsync(string fileName)
        {
            var fileDto = await _fileAppService.GetBlobAsync(new GetBlobRequestDto{ Name = fileName });

            return File(fileDto.Content, &quot;application/octet-stream&quot;, fileDto.Name);
        }
    }
}
</code></pre>
<p>As you see, <code>FileController</code> injects <code>IFileAppService</code> that we defined before. This controller has only one endpoint.</p>
<p><code>DownloadAsync</code> is using to send file from server to client.</p>
<p>This endpoint is requires only a <code>string</code> parameter, then we use that parameter to get stored blob. If blob is exist, we return a <code>File</code> result so download process can start.</p>
<h2>Creating User Interface</h2>
<p>We will create only one page to prove download and upload actions are working.</p>
<p>Create folder that name <code>Files</code> in your <code>Pages</code> folder at <code>FileActionsDemo.Web</code> project.</p>
<p>Create a Razor page that name <code>Index</code> with its model.</p>
<ul>
<li><code>Index.cshtml.cs</code></li>
</ul>
<pre><code class="language-csharp">using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;

namespace FileActionsDemo.Web.Pages.Files
{
    public class Index : AbpPageModel
    {
        [BindProperty]
        public UploadFileDto UploadFileDto { get; set; }

        private readonly IFileAppService _fileAppService;

        public bool Uploaded { get; set; } = false;

        public Index(IFileAppService fileAppService)
        {
            _fileAppService = fileAppService;
        }

        public void OnGet()
        {

        }

        public async Task&lt;IActionResult&gt; OnPostAsync()
        {
            using (var memoryStream = new MemoryStream())
            {
                await UploadFileDto.File.CopyToAsync(memoryStream);

                await _fileAppService.SaveBlobAsync(
                    new SaveBlobInputDto
                    {
                        Name = UploadFileDto.Name,
                        Content = memoryStream.ToArray()
                    }
                );
            }

            return Page();
        }
    }

    public class UploadFileDto
    {
        [Required]
        [Display(Name = &quot;File&quot;)]
        public IFormFile File { get; set; }

        [Required]
        [Display(Name = &quot;Filename&quot;)]
        public string Name { get; set; }
    }
}
</code></pre>
<p>As you see, we use <code>UploadFileDto</code> as a <code>BindProperty</code> and we inject <code>IFileAppService</code> to upload files.</p>
<p>The <code>UploadFileDto</code> is requires a <code>string</code> parameter for using as a blob name and a <code>IFormFile</code> that sent by user.</p>
<p>At the post action (<code>OnPostAsync</code>), if everything is well, we use <code>MemoryStream</code> to get all bytes from file content.</p>
<p>Then we save file with <code>SaveBlobAsync</code> method of <code>IFileAppService</code>.</p>
<ul>
<li><code>Index.cshtml</code></li>
</ul>
<pre><code class="language-csharp">@page
@model FileActionsDemo.Web.Pages.Files.Index

@section scripts{
    &lt;abp-script src=&quot;/Pages/Files/index.js&quot; /&gt;
}

&lt;abp-card&gt;
    &lt;abp-card-header&gt;
        &lt;h3&gt;File Upload and Download&lt;/h3&gt;
    &lt;/abp-card-header&gt;
    &lt;abp-card-body&gt;
        &lt;abp-row&gt;
            &lt;abp-column&gt;
                &lt;h3&gt;Upload File&lt;/h3&gt;
                &lt;hr /&gt;
                &lt;form method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
                    &lt;abp-input asp-for=&quot;UploadFileDto.Name&quot;&gt;&lt;/abp-input&gt;

                    &lt;abp-input asp-for=&quot;UploadFileDto.File&quot;&gt;&lt;/abp-input&gt;

                    &lt;input type=&quot;submit&quot; class=&quot;btn btn-info&quot; /&gt;
                &lt;/form&gt;
            &lt;/abp-column&gt;

            &lt;abp-column style=&quot;border-left: 1px dotted gray&quot;&gt;
                &lt;h3&gt;Download File&lt;/h3&gt;
                &lt;hr /&gt;
                &lt;form id=&quot;DownloadFile&quot;&gt;
                    &lt;div class=&quot;form-group&quot;&gt;
                        &lt;label for=&quot;fileName&quot;&gt;Filename&lt;/label&gt;&lt;span&gt; * &lt;/span&gt;
                        &lt;input type=&quot;text&quot; id=&quot;fileName&quot; name=&quot;fileName&quot; class=&quot;form-control &quot;&gt;
                    &lt;/div&gt;

                    &lt;input type=&quot;submit&quot; class=&quot;btn btn-info&quot;/&gt;
                &lt;/form&gt;
            &lt;/abp-column&gt;
        &lt;/abp-row&gt;
    &lt;/abp-card-body&gt;
&lt;/abp-card&gt;
</code></pre>
<p>We divided the page vertically, left side will be using for upload and right side will be using for download. We use <a href="https://docs.abp.io/en/abp/latest/UI/AspNetCore/Tag-Helpers/Index">ABP Tag Helpers</a> to create page.</p>
<ul>
<li><code>index.js</code></li>
</ul>
<pre><code class="language-javascript">$(function () {
  var DOWNLOAD_ENDPOINT = &quot;/download&quot;;

  var downloadForm = $(&quot;form#DownloadFile&quot;);

  downloadForm.submit(function (event) {
    event.preventDefault();

    var fileName = $(&quot;#fileName&quot;).val().trim();

    var downloadWindow = window.open(
      DOWNLOAD_ENDPOINT + &quot;/&quot; + fileName,
      &quot;_blank&quot;
    );
    downloadWindow.focus();
  });

  $(&quot;#UploadFileDto_File&quot;).change(function () {
    var fileName = $(this)[0].files[0].name;

    $(&quot;#UploadFileDto_Name&quot;).val(fileName);
  });
});
</code></pre>
<p>This jQuery codes are using for download. Also we wrote a simple code to autofill <code>Filename</code> input when user selects a file.</p>
<p>After creating razor page and js file, <code>FileActionsDemo.Web</code> project should be like as following image.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/web-project.png" alt="web-project" /></p>
<h2>Result</h2>
<p>After completing code tutorial, run <code>FileActionsDemo.Web</code> project and go <code>/Files</code>. You can upload any file with any name and also download those uploaded files.</p>
<p><img src="https://raw.githubusercontent.com/abpframework/abp/dev/docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/file-upload-result.gif" alt="file-up" /></p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/images/others/blank-cover-image-150_79.png" />
      <media:content url="https://abp.io/images/others/blank-cover-image-150_79.png" medium="image" />
    </item>
  </channel>
</rss>