Search code examples
c#angulartypescriptasp.net-coreng2-file-upload

Multiple form-data file upload parameters


I'd like to use the ng2-file-upload component on the client side and everything works so far but now I have to pass an additional parameter with every file that contains an identifier the file is attached to. I try to set the additionalParameter property of the FileUploader object in TypeScript:

this.uploader.options.additionalParameter = {"issueId": result.data.id};

On the server I have the following method that is working except I don't get the additional parameter (issueId) set above. (.NET Core 2.0)

public RequestResultModel File(IFormFile file);

The request payload contains the parameter but in a new form-data:

------WebKitFormBoundaryegtYAcYfO3gKdk9Z
Content-Disposition: form-data; name="file"; filename="81980085.pdf"
Content-Type: application/pdf


------WebKitFormBoundaryegtYAcYfO3gKdk9Z
Content-Disposition: form-data; name="issueId"

19
------WebKitFormBoundaryegtYAcYfO3gKdk9Z-- 

How can I modify the controller method in order to read the issueId parameter as well? In a previous project I used a second parameter public async Task<ApiResultBase> Upload(IFormFile file, string projectid) and it worked but now I would like to use this client side component because I don't want to suck with drag and drop and I'm lazy. I have tried to change the component's POST url after initialize (this.uploader.options.url = "/api/Issue/File/"+result.data.id;) but it tries to POST to the original address.


Solution

  • You are on track. I have a slightly different approach you can try out.On the client, try something like:

    this.uploader = new FileUploader({
                url: url,//The enpoint you are consuming
                additionalParameter: {
                    issueId: result.data.id //your parameter-remove quotes
                },
                headers: [{ name: 'Accept', value: 'application/json' }],//your custom header
                //autoUpload: true, //configure autoUpload
            });
    

    The library also has onErrorItem and onSuccessItem callbacks that you can leverage like below:

    this.uploader.onErrorItem = (item, response, status, headers) => this.onErrorItem(item, response, status, headers);
    this.uploader.onSuccessItem = (item, response, status, headers) => this.onSuccessItem(item, response, status, headers);
    

    Then(Optional) - Callbacks:

        onSuccessItem(item: FileItem, response: string, status: number, headers:ParsedResponseHeaders): any {
                //this gets triggered only once when first file is uploaded       
         }
        onErrorItem(item: FileItem, response: string, status: number, headers: 
               ParsedResponseHeaders): any {
                    let error = JSON.parse(response); //error server response            
                }
    

    On the API side you can restructure like below - Change it to your own signature.

     public async Task<IActionResult> UploadImages(MyFile upload)
    

    Then the MyFile model can be something like:

    public class MyFile
        {
            public string issueId { get; set; }        
            public IFormFile File { get; set; }
        }
    

    To get the param and the file:

    var file = upload.File //This is the IFormFile file
    var param = upload.issueId //param
    

    To save the file to disk:

    using (var stream = new FileStream(path, FileMode.Create))
            {
                await file.File.CopyToAsync(stream);
            }