uploading image with angular2 to asp.net core

2020-07-13 09:01发布

So I have asp.net core application with angular2. Now I want to upload image and I managed to do so if I upload it as byte[]. But then I can't check if file is realy image in backend so I tried to look for another solutions.. I found this blog about file upload: https://devblog.dymel.pl/2016/09/02/upload-file-image-angular2-aspnetcore/ but it does not work for me...

For file upload I use angular2 library angular2-image-upload, so my html part of image upload looks like this:

<image-upload [max]="1" [buttonCaption]="'Browse'" [preview]="false" (change)="onFileChange($event)"></image-upload>
<button (click)="onSaveChanges()" class="btn btn-primary float-left" type="submit">Save</button>

then my angular2 part looks like this:

onFileChange(event: any) {
    this.file = event.target.files[0];

    if (event.target.files && this.file) {
        var reader = new FileReader();

        reader.onload = (event: any) => {
            this.profilePicture = event.target.result;
        }
        reader.readAsDataURL(this.file);
    }
}

onSaveChanges() {
    this.isSaving = true;
    this.country = this.state.user.country;
    this.userService.userChange(this.firstName, this.lastName, this.country, this.file, this.currentPassword, this.newPassword).subscribe(success => {
        this.state.user.profilePicture = this.profilePicture;
        this.state.user.firstName = this.firstName;
        this.state.user.lastName = this.lastName;
        this.isSaving = false;
        this.passwordError = false;
        this.isSuccessful = true;
        this.currentPassword = '';
        this.newPassword = '';
        this.newPasswordRepeat = '';
    }, error => {
        this.isSaving = false;
        this.passwordError = true;
        this.passwordErrorMessage = error._body;
    });
}

my angular2 api call looks like this:

userChange(firstName: string, lastName: string, country: string, file: File, oldPassword: string, newPassword: string) {
    let input = new FormData();
    input.append("File", file);

    var url = this.baseUrl + "updateUser";

    return this.http.post(url, { FirstName: firstName, LastName: lastName, Country: country, File: input, OldPassword: oldPassword, NewPassword: newPassword });
}

my asp.net core controller (NOTE that I'm not showing the body of controller because it is irrelevant):

[HttpPost]
public async Task<IActionResult> UpdateUser([FromBody]UserChange userChange)
{ 
}

UserChange class:

public class UserChange
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string Country { get; set; }

    public IFormFile File { get; set; }

    public string OldPassword { get; set; }

    public string NewPassword { get; set; }
}

The thing is that when I submit my image, I always getting my UserChange object as null. When I was adding image as byte[] it worked like a charm what is the problem?? Why I am getting always null even if I'm passing the file that is not null? Other thing that I saw that when I change the type from IFormFile to FormFile my UserChange object is not null anymore but only File parameter from object is throwing this error

'userChange.File.ContentDisposition' threw an exception of type 'System.NullReferenceException'

UPDATE 1

Somehow I managed to send file to asp.net controller using this answer: File upload using Asp.Net Core Web API file is always null but to do so I had to create another action that has no parameters, but how to send file in my situation is still unknown...

2条回答
▲ chillily
2楼-- · 2020-07-13 09:17

I found your post when looking to do something similar, and also started out using the angular2-image-upload library, but decided to attempt the simple approach first. For my purposes I was only needing the byte[] image file (will attempt adding form fields for image title and caption next), and found some of what Michael Dymel suggests in his blog post here to be very helpful and got it working. Despite that you've not been able to get your approach to work, it has helped me a lot.

Where I came unstuck was with configuration of the correct routing, and for a time it looked like my file was being correctly picked up on the angular service, but was null when it got to the 'Upload' controller. Once I checked that the path from the upload service and the path defined in the [Route("/api/upload")] attribute of the controller were the same, it all fell into place and I was able to upload successfully. Slightly different issue to what you've had, but this worked for me:

My Upload component method:

addFile(): void {
    let fi = this.fileInput.nativeElement;
    if (fi.files && fi.files[0]) {
        let fileToUpload = fi.files[0];

        if (fileToUpload) {
            this.uploadService.upload(fileToUpload)
            .subscribe(res => {
                console.log(res);
            });
        }
        else
            console.log("FileToUpload was null or undefined.");
    }
}

Calls the Upload service:

upload(fileToUpload: any) {
        let input = new FormData();
        input.append("fileForController", fileToUpload);
        return this.http.post("/api/upload", input );
    }

Which Posts to the 'Upload' ActionResult of my ImagesController, which looks like this (I'm saving my image to a database, so the 'path' variable is actually redundant). The 'Image' object is just a straightforward model for the Url, Title, ImageFileContent and Caption fields:

[HttpPost]
[Route("/api/upload")]
public async Task<IActionResult> Upload(IFormFile fileForController)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    if (fileForController == null) throw new Exception("File is null");
    if (fileForController.Length == 0) throw new Exception("File is empty");

    var theImage = new Image();
    var path = Path.Combine("~\\ClientApp\\app\\assets\\images\\", fileForController.FileName);

    theImage.Url = path + fileForController.FileName;
    theImage.Title = fileForController.Name + "_" + fileForController.FileName;
    theImage.Caption = fileForController.Name + " and then a Surprice Caption";

    using (Stream stream = fileForController.OpenReadStream())
    {
        using (var reader = new BinaryReader(stream))
        {
            var fileContent = reader.ReadBytes((int)fileForController.Length);

            theImage.ImageFileContent = fileContent;
            _context.Images.Add(theImage);

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
    }
    return Ok(theImage);
}

And my html template fields were almost exactly as per Michael Dymel's post:

<form action="gallery" name="fileUploadForm" method="post" enctype="multipart/form-data">
    <div class="col-md-6">
        <input #fileInput type="file" title="Choose Image to upload" />
        <button (click)="addFile()" class="btn btn-success">Add</button>
    </div>
</form>
查看更多
Animai°情兽
3楼-- · 2020-07-13 09:26

You are cannot send binary data to server with json, because Json not supported binary data.

Description Here

But you can try send 'multipart/form-data'

Example Code .net Core Api:

 public class FormModel
 {
        public int Id { get; set; }
        public string Title{ get; set; }
        public string Url { get; set; }
        public bool IsDeleted { get; set; }
        public IFormFile File { get; set; }
 }

 [HttpPost]
 public IActionResult Post([FromForm] FormModel formModel)
 {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        //** your server side code **//

        return Ok();
  }

Example Code Angular Http Post Your Form Data:

  const formData = new FormData();
  formData.append('File', this.file[0] // your input file data);
  formData.append('Id', this.Id.value // your form id value);
  formData.append('Title', this.Title.value // your form title value);
  formData.append('Url', this.Url.value // your form url value)

  const options = new RequestOptions();
  options.headers = new Headers();
  options.headers.append('enctype', 'multipart/form-data');

  this.http.post('your api url', formData, options) {
     // your return value
  }
查看更多
登录 后发表回答