Browse Source

Add community articles.

pull/5033/head
Halil İbrahim Kalkan 6 years ago
parent
commit
95f1346600
  1. 462
      docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/POST.md
  2. BIN
      docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/chat.png
  3. BIN
      docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/dataflow-diagram.png
  4. BIN
      docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/header.png
  5. BIN
      docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/login1.png
  6. BIN
      docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/login2.png
  7. BIN
      docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/new-user.png
  8. 396
      docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/POST.md
  9. BIN
      docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/application-contracts-project.png
  10. BIN
      docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/file-upload-result.gif
  11. BIN
      docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/initial-project.png
  12. BIN
      docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/web-project.png

462
docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/POST.md

@ -0,0 +1,462 @@
# Real Time Messaging In A Distributed Architecture Using Abp Framework, SingalR & RabbitMQ
In this article, we will build a basic real time messaging application in a distributed architecture. We will use [Abp Framework](https://abp.io) for infrastructure and tiered startup template, [SignalR](https://dotnet.microsoft.com/apps/aspnet/signalr) for real time server-client communication and [RabbitMQ](https://www.rabbitmq.com/) as the distributed event bus.
When Web & API tiers are separated, it is impossible to directly send a server-to-client message from the HTTP API. This is also true for a microservice architected application. We suggest to use the distributed event bus to deliver the message from API application to the web application, then to the client.
![data flow](dataflow-diagram.png)
Above, you can see the data-flow that we will implement in this article. This diagram represents how data will flow in our application when **Client 1** sends a message to **Client 2**. It is explained in 5 steps:
1. **Client 1** sends a message data to **Web Application** via REST call.
2. **Web Application** redirects the message data to **Http Api**.
3. The message data is processed in **Http Api** and **Http Api** publishes an event that holds the data that will be sent to **Client 2**.
4. **Web application**, that is subscribed to that event, receives it.
5. **Web Application** sends the message to **Client 2**.
For this example flow, we could send message from **Client 1** to **Client 2** directly on the **SignalR Hub**. However, what we are trying here to demonstrate is sending a real-time message from the **Http Api** to a specific user who is connected to the web application.
## Implementation
### Startup template and initial run
[Abp Framework](www.abp.io) offers startup templates to get into the business faster. We can download a new tiered startup template using [Abp CLI](https://docs.abp.io/en/abp/latest/CLI):
`abp new SignalRTieredDemo --tiered`
After download is finished, we run ***.DbMigrator** project to create the database and seed initial data (admin user, role etc). Then we run ***.IdentityServer**, ***.HttpApi.Host** and ***.Web** to see our application working.
### Creating Application Layer
We create an [application service](https://docs.abp.io/en/abp/latest/Application-Services) that publishes the message as event.
In ***.Application.Contracts** project:
````csharp
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace SignalRTieredDemo
{
public interface IChatAppService : IApplicationService
{
Task SendMessageAsync(SendMessageInput input);
}
}
````
Input DTO for SendMessageAsync method:
````csharp
namespace SignalRTieredDemo
{
public class SendMessageInput
{
public string TargetUserName { get; set; }
public string Message { get; set; }
}
}
````
Event transfer object (ETO) for communication on event bus:
````csharp
using System;
namespace SignalRTieredDemo
{
public class ReceivedMessageEto
{
public string ReceivedText { get; set; }
public Guid TargetUserId { get; set; }
public string SenderUserName { get; set; }
public ReceivedMessageEto(
Guid targetUserId, string senderUserName, string receivedText)
{
ReceivedText = receivedText;
TargetUserId = targetUserId;
SenderUserName = senderUserName;
}
}
}
````
In ***.Application** project:
````csharp
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Identity;
namespace SignalRTieredDemo
{
public class ChatAppService: SignalRTieredDemoAppService, IChatAppService
{
private readonly IIdentityUserRepository _identityUserRepository;
private readonly ILookupNormalizer _lookupNormalizer;
private readonly IDistributedEventBus _distributedEventBus;
public ChatAppService(IIdentityUserRepository identityUserRepository, ILookupNormalizer lookupNormalizer, IDistributedEventBus distributedEventBus)
{
_identityUserRepository = identityUserRepository;
_lookupNormalizer = lookupNormalizer;
_distributedEventBus = distributedEventBus;
}
public async Task SendMessageAsync(SendMessageInput input)
{
var targetId = (await _identityUserRepository.FindByNormalizedUserNameAsync(_lookupNormalizer.NormalizeName(input.TargetUserName))).Id;
await _distributedEventBus.PublishAsync(new ReceivedMessageEto(targetId, CurrentUser.UserName, input.Message));
}
}
}
````
### Creating API Layer
We create an endpoint for sending message that redirects the process to application layer:
In **controllers** folder of ***.HttpApi** project:
````csharp
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace SignalRTieredDemo.Controllers
{
[Route("api/app/chat")]
public class ChatController : AbpController, IChatAppService
{
private readonly IChatAppService _chatAppService;
public ChatController(IChatAppService chatAppService)
{
_chatAppService = chatAppService;
}
[HttpPost]
[Route("send-message")]
public async Task SendMessageAsync(SendMessageInput input)
{
await _chatAppService.SendMessageAsync(input);
}
}
}
````
### Adding SignalR
To add SignalR to our solution, we add `Volo.Abp.AspNetCore.SignalR` nuget package to ***.Web** project.
And then add `AbpAspNetCoreSignalRModule` dependency:
````csharp
namespace SignalRTieredDemo.Web
{
[DependsOn(
...
typeof(AbpAspNetCoreSignalRModule) // <---
)]
public class SignalRTieredDemoWebModule : AbpModule
{
````
Also, we need to add [@abp/signalr](https://www.npmjs.com/package/@abp/signalr) npm package to package.json in ***.Web** project, then run **yarn** and **gulp** commands.
`````json
{
.
.
"dependencies": {
.
.
"@abp/signalr": "^2.9.0"
}
}
`````
*Remember to add the latest package version.*
You can find more information for Abp SignalR Integration on [the related document](https://docs.abp.io/en/abp/latest/SignalR-Integration).
### Creating A Hub
We need a hub for SignalR connection. We can inherit it from `AbpHup` base class.
In ***.Web** project:
````csharp
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.AspNetCore.SignalR;
namespace SignalRTieredDemo.Web
{
[Authorize]
public class ChatHub : AbpHub
{
}
}
````
While you could inherit from the standard `Hub` class, `AbpHub` has some common services pre-injected as base properties, which is useful on your development.
### Adding & Configuring RabbitMQ
To add RabbitMQ to our solution, we add `Volo.Abp.EventBus.RabbitMQ` nuget package to ***.HttpApi.Host** and ***.Web** projects.
Launch a **command line**, navigate to directory where ***.HttpApi.Host.csproj** file exist, and run the command below using [Abp CLI](https://docs.abp.io/en/abp/latest/CLI):
````bash
abp add-package Volo.Abp.EventBus.RabbitMQ
````
Then do the same for ***.Web** project.
After we add the package, we configure RabbitMQ by adding configuration in **appsettings.json** files of those projects.
For ***.HttpApi.Host** project:
````json
{
...
"RabbitMQ": {
"Connections": {
"Default": {
"HostName": "localhost"
}
},
"EventBus": {
"ClientName": "SignalRTieredDemo_HttpApi",
"ExchangeName": "SignalRTieredDemoTest"
}
},
...
}
````
For ***.Web** project:
````json
{
...
"RabbitMQ": {
"Connections": {
"Default": {
"HostName": "localhost"
}
},
"EventBus": {
"ClientName": "SignalRTieredDemo_Web",
"ExchangeName": "SignalRTieredDemoTest"
}
},
...
}
````
### Handling New Message Event
Once we publish a new message event from `Http Api`, we must to handle it in `Web Application`. Therefore we need an event handler in ***.Web** Project:
````csharp
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
namespace SignalRTieredDemo.Web
{
public class ReceivedMessageEventHandler :
IDistributedEventHandler<ReceivedMessageEto>,
ITransientDependency
{
private readonly IHubContext<ChatHub> _hubContext;
public ReceivedMessageEventHandler(IHubContext<ChatHub> hubContext)
{
_hubContext = hubContext;
}
public async Task HandleEventAsync(ReceivedMessageEto eto)
{
var message = $"{eto.SenderUserName}: {eto.ReceivedText}";
await _hubContext.Clients
.User(eto.TargetUserId.ToString())
.SendAsync("ReceiveMessage", message);
}
}
}
````
### Creating Chat Page
We create the files below in **Pages** folder of ***.Web** Project.
**Chat.cshtml**:
````html
@page
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.SignalR
@model SignalRTieredDemo.Web.Pages.ChatModel
@section styles {
<abp-style src="/Pages/Chat.css" />
}
@section scripts {
<abp-script type="typeof(SignalRBrowserScriptContributor)" />
<abp-script src="/Pages/Chat.js" />
}
<h1>Chat</h1>
<div>
<abp-row>
<abp-column size-md="_6">
<div>All Messages:</div>
<ul id="MessageList" style="">
</ul>
</abp-column>
<abp-column size-md="_6">
<form>
<abp-row>
<abp-column>
<label for="TargetUser">Target user:</label>
<input type="text" id="TargetUser" />
</abp-column>
</abp-row>
<abp-row class="mt-2">
<abp-column>
<label for="Message">Message:</label>
<textarea id="Message" rows="4"></textarea>
</abp-column>
</abp-row>
<abp-row class="mt-2">
<abp-column>
<abp-button type="submit" id="SendMessageButton" button-type="Primary" size="Block" text="SEND!" />
</abp-column>
</abp-row>
</form>
</abp-column>
</abp-row>
</div>
````
**Chat.cshtml.cs**:
````csharp
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace SignalRTieredDemo.Web.Pages
{
public class ChatModel : PageModel
{
public void OnGet()
{
}
}
}
````
**Chat.css**:
````css
#MessageList {
border: 1px solid gray;
height: 400px;
overflow: auto;
list-style: none;
padding-left: 0;
padding: 10px;
}
#TargetUser {
width: 100%;
}
#Message {
width: 100%;
}
````
**Chat.js**:
````javascript
$(function () {
var connection = new signalR.HubConnectionBuilder().withUrl("/signalr-hubs/chat").build();
connection.on("ReceiveMessage", function (message) {
console.log(message);
$('#MessageList').append('<li><strong><i class="fas fa-long-arrow-alt-right"></i> ' + message + '</strong></li>');
});
connection.start().then(function () {
}).catch(function (err) {
return console.error(err.toString());
});
$('#SendMessageButton').click(function (e) {
e.preventDefault();
var targetUserName = $('#TargetUser').val();
var message = $('#Message').val();
$('#Message').val('');
signalRTieredDemo.controllers.chat.sendMessage({
targetUserName: targetUserName,
message: message
}).then(function() {
$('#MessageList')
.append('<li><i class="fas fa-long-arrow-alt-left"></i> ' + abp.currentUser.userName + ': ' + message + '</li>');
});
});
});
````
Then we can add this new page to menu on ***MenuContributor.cs** in **Menus** folder:
````csharp
...
public class SignalRTieredDemoMenuContributor : IMenuContributor
{
...
private Task ConfigureMainMenuAsync(MenuConfigurationContext context)
{
...
context.Menu.Items.Add(new ApplicationMenuItem("SignalRDemo.Chat", "Chat", "/Chat")); // <-- We add this line
return Task.CompletedTask;
}
...
}
````
## Running & Testing
We run ***.IdentityServer**, ***.HttpApi.Host** and ***.Web** in order. After ***.Web** project is ran, firstly login with `admin` username and `1q2w3E*` password.
![click on login](login1.png)
![login with `admin` username and `1q2w3E*` password.](login2.png)
After we login, go to `/Identity/Users` page and create a new user. So that we can chat with them.
![create a new user](new-user.png)
Then we open the application in another browser and login with the user we created above. Now we can go to chat page and start messaging:
![messaging](chat.png)
We can test with more user. All sent and incoming messages are displayed in the left box.
### Source code
Source code of the final application can be found on the [GitHub repository](https://github.com/abpframework/abp-samples/tree/master/SignalRTieredDemo).

BIN
docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/chat.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/dataflow-diagram.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/header.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/login1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/login2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/en/Community-Articles/2020-05-29-Real-Time-Messaging-In-A-Distributed-Architecture-Using-Abp-Framework-SingalR-RabbitMQ/new-user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

396
docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/POST.md

@ -0,0 +1,396 @@
# File Upload/Download with BLOB Storage System in ASP.NET Core & ABP Framework
## Introduction
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 & ABP Framework. By following this article, you will create a web project and its related code to upload and download files.
Before the creating application, we need to know some fundamentals.
## BLOB Storing
It is typical to **store file contents** in an application and read these file contents on need. Not only files, but you may also need to save various types of **large binary objects**, a.k.a. [BLOB](https://en.wikipedia.org/wiki/Binary_large_object)s, into a **storage**. For example, you may want to save user profile pictures.
A BLOB is a typically **byte array**. There are various places to store a BLOB item; storing in the local file system, in a shared database or on the [Azure BLOB storage](https://azure.microsoft.com/en-us/services/storage/blobs/) can be options.
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;
- You can **easily integrate** to your favorite BLOB storage provides with a few lines of configuration.
- You can then **easily change** your BLOB storage without changing your application code.
- If you want to create **reusable application modules**, you don't need to make assumption about how the BLOBs are stored.
ABP BLOB Storage system is also compatible to other ABP Framework features like [multi-tenancy](https://docs.abp.io/en/abp/latest/Multi-Tenancy).
To get more information about ABP BLOB Storing system, please check this [documentation](https://docs.abp.io/en/abp/latest/Blob-Storing).
## Preparing the Project
### Startup template and the initial run
Abp Framework offers startup templates to get into the business faster. We can download a new startup template using Abp CLI:
`abp new FileActionsDemo -m none`
After the download is finished, we run `FileActionsDemo.DbMigrator` project to create the database and seed initial data (admin user, role, etc). Then we run `FileActionsDemo.Web` to see our application working.
> _Default admin username is **admin** and password is **1q2w3E\***_
![initial-project](initial-project.png)
### Adding Blob Storing Module
For this article, we use [Blob Storing Database Provider](https://docs.abp.io/en/abp/latest/Blob-Storing-Database).
You can use [Azure](https://docs.abp.io/en/abp/latest/Blob-Storing-Azure) or [File System](https://docs.abp.io/en/abp/latest/Blob-Storing-File-System) providers also.
Open a command prompt (terminal) in the folder containing your solution (.sln) file and run the following command:
`abp add-module Volo.Abp.BlobStoring.Database`
This action will add the module depencies and also module migration. After this action, run `FileActionsDemo.DbMigrator` to update the database.
### Setting up Blob Storaging
BLOB Strorage system works with `Containers`. Before the using blob storage, we need to create our blob container.
Create a class that name `MyFileContainer` at the `FileActionsDemo.Domain` project.
```csharp
using Volo.Abp.BlobStoring;
namespace FileActionsDemo
{
[BlobContainerName("my-file-container")]
public class MyFileContainer
{
}
}
```
That's all, we can start to use BLOB storing in our application.
## Creating Application Layer
Before the creating Application Service, we need to create some [DTO](https://docs.abp.io/en/abp/latest/Data-Transfer-Objects)s that used by Application Service.
Create following DTOs in `FileActionsDemo.Application.Contracts` project.
- `BlobDto.cs`
```csharp
namespace FileActionsDemo
{
public class BlobDto
{
public byte[] Content { get; set; }
public string Name { get; set; }
}
}
```
- `GetBlobRequestDto.cs`
```csharp
using System.ComponentModel.DataAnnotations;
namespace FileActionsDemo
{
public class GetBlobRequestDto
{
[Required]
public string Name { get; set; }
}
}
```
- `SaveBlobInputDto.cs`
```csharp
using System.ComponentModel.DataAnnotations;
namespace FileActionsDemo
{
public class SaveBlobInputDto
{
public byte[] Content { get; set; }
[Required]
public string Name { get; set; }
}
}
```
Create `IFileAppService.cs` interface at the same place with DTOs.
- `IFileAppService`
```csharp
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace FileActionsDemo
{
public interface IFileAppService : IApplicationService
{
Task SaveBlobAsync(SaveBlobInputDto input);
Task<BlobDto> GetBlobAsync(GetBlobRequestDto input);
}
}
```
After creating DTOs and interface, `FileActionsDemo.Application.Contracts` project should be like as following image.
![application-contracts-project](application-contracts-project.png)
Then we can create our Application Service.
Create `FileAppService.cs` in `FileActionsDemo.Application` project.
```csharp
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.BlobStoring;
namespace FileActionsDemo
{
public class FileAppService : ApplicationService, IFileAppService
{
private readonly IBlobContainer<MyFileContainer> _fileContainer;
public FileAppService(IBlobContainer<MyFileContainer> fileContainer)
{
_fileContainer = fileContainer;
}
public async Task SaveBlobAsync(SaveBlobInputDto input)
{
await _fileContainer.SaveAsync(input.Name, input.Content, true);
}
public async Task<BlobDto> GetBlobAsync(GetBlobRequestDto input)
{
var blob = await _fileContainer.GetAllBytesAsync(input.Name);
return new BlobDto
{
Name = input.Name,
Content = blob
};
}
}
}
```
As you see in previous code block, we inject `IBlobContainer<MyFileContainer>` to our app service. It will handle all blob actions for us.
- `SaveBlobAsync` method uses `SaveAsync` of `IBlobContainer<MyFileContainer>` 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 `true` for `overrideExisting` parameter.
- `GetBlobAsync` method is uses `GetAllBytesAsync` of `IBlobContainer<MyFileContainer>` to get blob content by name.
We finished the application layer for this project. After that we will create a `Controller` for API and `Razor Page` for UI.
## Creating Controller
Create `FileController.cs` in your `FileActionsDemo.HttpApi` project.
```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("download/{fileName}")]
public async Task<IActionResult> DownloadAsync(string fileName)
{
var fileDto = await _fileAppService.GetBlobAsync(new GetBlobRequestDto{ Name = fileName });
return File(fileDto.Content, "application/octet-stream", fileDto.Name);
}
}
}
```
As you see, `FileController` injects `IFileAppService` that we defined before. This controller has only one endpoint.
`DownloadAsync` is using to send file from server to client.
This endpoint is requires only a `string` parameter, then we use that parameter to get stored blob. If blob is exist, we return a `File` result so download process can start.
## Creating User Interface
We will create only one page to prove download and upload actions are working.
Create folder that name `Files` in your `Pages` folder at `FileActionsDemo.Web` project.
Create a Razor page that name `Index` with its model.
- `Index.cshtml.cs`
```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<IActionResult> 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 = "File")]
public IFormFile File { get; set; }
[Required]
[Display(Name = "Filename")]
public string Name { get; set; }
}
}
```
As you see, we use `UploadFileDto` as a `BindProperty` and we inject `IFileAppService` to upload files.
The `UploadFileDto` is requires a `string` parameter for using as a blob name and a `IFormFile` that sent by user.
At the post action (`OnPostAsync`), if everything is well, we use `MemoryStream` to get all bytes from file content.
Then we save file with `SaveBlobAsync` method of `IFileAppService`.
- `Index.cshtml`
```csharp
@page
@model FileActionsDemo.Web.Pages.Files.Index
@section scripts{
<abp-script src="/Pages/Files/index.js" />
}
<abp-card>
<abp-card-header>
<h3>File Upload and Download</h3>
</abp-card-header>
<abp-card-body>
<abp-row>
<abp-column>
<h3>Upload File</h3>
<hr />
<form method="post" enctype="multipart/form-data">
<abp-input asp-for="UploadFileDto.Name"></abp-input>
<abp-input asp-for="UploadFileDto.File"></abp-input>
<input type="submit" class="btn btn-info" />
</form>
</abp-column>
<abp-column style="border-left: 1px dotted gray">
<h3>Download File</h3>
<hr />
<form id="DownloadFile">
<div class="form-group">
<label for="fileName">Filename</label><span> * </span>
<input type="text" id="fileName" name="fileName" class="form-control ">
</div>
<input type="submit" class="btn btn-info"/>
</form>
</abp-column>
</abp-row>
</abp-card-body>
</abp-card>
```
We divided the page vertically, left side will be using for upload and right side will be using for download. We use [ABP Tag Helpers](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Tag-Helpers/Index) to create page.
- `index.js`
```javascript
$(function () {
var DOWNLOAD_ENDPOINT = "/download";
var downloadForm = $("form#DownloadFile");
downloadForm.submit(function (event) {
event.preventDefault();
var fileName = $("#fileName").val().trim();
var downloadWindow = window.open(
DOWNLOAD_ENDPOINT + "/" + fileName,
"_blank"
);
downloadWindow.focus();
});
$("#UploadFileDto_File").change(function () {
var fileName = $(this)[0].files[0].name;
$("#UploadFileDto_Name").val(fileName);
});
});
```
This jQuery codes are using for download. Also we wrote a simple code to autofill `Filename` input when user selects a file.
After creating razor page and js file, `FileActionsDemo.Web` project should be like as following image.
![web-project](web-project.png)
## Result
After completing code tutorial, run `FileActionsDemo.Web` project and go `/Files`. You can upload any file with any name and also download those uploaded files.
![file-up](file-upload-result.gif)

BIN
docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/application-contracts-project.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/file-upload-result.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/initial-project.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
docs/en/Community-Articles/2020-07-21-File-Upload-Download-With-BLOB-Storage-System-in-ASPNET-Core-ABP-Framework/web-project.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Loading…
Cancel
Save