table of Contents
Introduction
I was recently working on a website that needs to upload and download files. It seems that for some people, is a place of torment, so I thought I should write a small article ( since my last article has been too long, has expired a long time ) , both for my own offspring, also for other things.
background
I'm building an application that stores sales of digital products. All front-end with VueJS written with a ASP.NET Core backend API , for providing file and SPA .
Using the code
The code is easy to explain, so I will only briefly describes the function of each module, rather than the detailed description of all content and mess up the topic.
Register your API
main.ts
Import your api module
import api from './services/api'; -- this is my sites api
...
Vue.prototype.$api = api;
api-plugin.d.ts
The $ api variables attached to the Vue and assign Api type.
import { Api } from './services/api';
declare module 'vue/types/vue' {
interface Vue {
$api: Api;
}
}
export default Api;
upload files
In my VueJS assembly, I data () creates a variable internal object used to save the file to be sent to the server.
files: new FormData(),
I added the user to respond to a handler method to add files to upload files
handleFileUpload(fileList: any) {
this.files.append('file', fileList[0], fileList[0].name);
},
Vue component template contains the file input element
<input type="file" v-on:change="handleFileUpload($event.target.files)" />
Submission
Then, when a user in your UI when performing operations triggered upload, I will call my API .
this.$api.uploadFile(this.files)
.then((response: <<YourResponseType>>) => {
this.hasError = false;
this.message = 'File uploaded';
}).catch((error) => {
this.hasError = true;
this.message = 'Error uploading file';
});
API service methods
In the above tern assembly method that appears in my API calls this method on the service.
public async uploadFile(fileData: FormData): Promise<<<YourResponseType>>> {
return await axios.post('/api/to/your/upload/handler', fileData, { headers: { 'Content-Type': 'multipart/form-data' } })
.then((response: any) => {
return response.data;
})
.catch((error: any) => {
throw new Error(error);
});
}
ASP.NET Core API method
The code in the method according to your own requirements are very different, but the basic structure looks like this.
[HttpPost("/api/to/your/upload/handler")]
[Consumes("multipart/form-data")]
public async Task<IActionResult> UploadHandler(IFormCollection uploads)
{
if (uploads.Files.Length <= 0) { return BadRequest("No file data"); }
foreach (var f in uploads.Files)
{
var filePath = "YourGeneratedUniqueFilePath";
using (var stream = System.IO.File.Create(filePath))
{
await file.CopyToAsync(stream);
}
}
return Ok();
}
download file
The start from the server side, your API method is shown below. Since I use Kestral since, I chose to use the ControllerBase.PhysicalFile () method, but if your controller is more suitable for your needs, then the controller as well as basic controller method that returns ControllerBase.File () .
Since I upload and store my data associated with an entity, it is required by the ID request value download, but you can use any method that suits your needs.
[HttpGet("[action]")]
public async Task<IActionResult> GetFile(string id)
{
var dir = "GetOrBuildYourDirectoryString";
var fileName = "GetYourFileName";
var mimeType = GetMimeType(fileName);
var path = Path.Combine(dir, fileName);
return PhysicalFile(path, mimeType, version.FileName);
}
public string GetMimeType(string fileName)
{
var provider = new FileExtensionContentTypeProvider();
string contentType;
if (!provider.TryGetContentType(fileName, out contentType))
{
contentType = "application/octet-stream";
}
return contentType;
}
Note: FileExtensionContentTypeProvider types from Microsoft.AspNetCore.StaticFiles NuGet package
Install-Package Microsoft.AspNetCore.StaticFiles -Version 2.2.0
The client API download
To call this on the server GetFile () method, our client API service needs for public download method. This would make things get a little tricky. You may have to configure the server to provide and / or disclosure of disposition header. This is somewhat beyond the scope of this article, because I want to keep it simple.
I do not need to perform any special steps to access this header, but I do have to perform some operation to extract the required client data - mainly the file name. Unfortunately, this code is not particularly good. If anyone has any suggestions on how it improved, please let me know.
public async downloadFile(id: string): Promise<void> {
return await axios.get('/api/download/getfile?id=' + id, { responseType : 'blob' } )
.then((response: any) => {
const disposition = response.headers['content-disposition'];
let fileName = '';
if (disposition && disposition.indexOf('attachment') !== -1) {
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
const matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
fileName = matches[1].replace(/['"]/g, '');
}
}
const fileUrl = window.URL.createObjectURL(new Blob([response.data]));
const fileLink = document.createElement('a');
fileLink.href = fileUrl;
fileLink.setAttribute('download', fileName);
document.body.appendChild(fileLink);
fileLink.click();
})
.catch((error: any) => {
throw new Error(error);
});
}
This will set the client to download and open the local user file save dialog conventional browser.
Protecting uploaded files
In view of the above code is " manual " process of downloading and uploading files, it can be considered, browser HTML simple file in URL is not an ideal situation. In my example, I uploaded files to be processed on the download directory.
In order to protect this " downloads " directory, I was in the StartUp.cs Configure a small amount of logic will be mapped to the method of the .NET Core IApplicationBuilder instance. It will intercept this URL any request and sends a 401 response.
app.Map("/downloads", subApp => {
subApp.Use(async (context, next) =>
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
});
});
Any attempt to access the downloads person files in the directory will be guided, the browser will receive an error response from the server.
I hope you can find a brief description of some use in this method.