Download TypeScript and ASP.NET Core processing file uploads and protected

table of Contents

Introduction

background

Using the code

Register your API

main.ts

api-plugin.d.ts

upload files

Submission

API service methods

ASP.NET Core API method

download file

Client API download

Protecting uploaded files


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.

Published 71 original articles · won praise 152 · views 520 000 +

Guess you like

Origin blog.csdn.net/mzl87/article/details/105082237