Reputation: 23
I am trying to build a file upload with the aspnetboilerplate .net core and angular spa. I am new to the framework and really like it but can't get the file upload working.
First if this can be done in a service without a controller that would be my preferred solution.
Currently I am trying a controller with the code below but it doesn't work
Here is .ts component method to upload file.
upload(files) {
if (files.length === 0)
return;
const formData = new FormData();
for (let file of files)
formData.append(file.name, file);
const uploadReq = new HttpRequest('POST', `api/FileUpload/UploadFile`, formData, {
reportProgress: true,
});
this.http.request(uploadReq).subscribe(event => {
if (event.type === HttpEventType.UploadProgress)
this.progress = Math.round(100 * event.loaded / event.total);
else if (event.type === HttpEventType.Response)
this.reqMessage = event.body.toString();
});
}
I put the controller in the myapp web host controller folder (The same folder where the antiforgery controller exist)
Here is my controller
[Route("api/[controller]")]
[ApiController]
public class FileUploadController : myappControllerBase
{
private IHostingEnvironment _hostingEnvironment;
public FileUploadController(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
[HttpPost, DisableRequestSizeLimit]
public ActionResult UploadFile()
{
try
{
var file = Request.Form.Files[0];
string folderName = "Upload";
string webRootPath = _hostingEnvironment.WebRootPath;
string newPath = Path.Combine(webRootPath, folderName);
if (!Directory.Exists(newPath))
{
Directory.CreateDirectory(newPath);
}
if (file.Length > 0)
{
string fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
string fullPath = Path.Combine(newPath, fileName);
using (var stream = new FileStream(fullPath, FileMode.Create))
{
file.CopyTo(stream);
}
}
return Json("Upload Successful.");
}
catch (System.Exception ex)
{
return Json("Upload Failed: " + ex.Message);
}
}
}
Not sure if the formData is setup correct but it does not reach my controller.
Upvotes: 2
Views: 1652
Reputation: 9634
Check out the sample code that uploads profile picture...
UploadProfilePicture() controller method:
public class FileUploadController : myappControllerBase
{
public UploadProfilePictureOutput UploadProfilePicture()
{
try
{
var profilePictureFile = Request.Form.Files.First();
//Check input
if (profilePictureFile == null)
{
throw new UserFriendlyException("An error occurred!");
}
if (profilePictureFile.Length > 9999999) //change the max value
{
throw new UserFriendlyException("Picture exceeds max size!));
}
byte[] fileBytes;
using (var stream = profilePictureFile.OpenReadStream())
{
fileBytes = stream.GetAllBytes();
}
//Save new picture
var fileInfo = new FileInfo(profilePictureFile.FileName);
var tempFileName = Guid.NewGuid().ToString() + fileInfo.Extension;
var tempFilePath = Path.Combine(@"c:\temp\upload", tempFileName);
System.IO.File.WriteAllBytes(temp1FilePath, fileBytes);
using (var bmpImage = new Bitmap(tempFilePath))
{
return new UploadProfilePictureOutput
{
FileName = tempFileName,
Width = bmpImage.Width,
Height = bmpImage.Height
};
}
}
catch (UserFriendlyException ex)
{
return new UploadProfilePictureOutput(new ErrorInfo(ex.Message));
}
}
}
}
UploadProfilePictureOutput.cs:
public class UploadProfilePictureOutput : ErrorInfo
{
public string FileName { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public UploadProfilePictureOutput()
{
}
public UploadProfilePictureOutput(ErrorInfo error)
{
Code = error.Code;
Details = error.Details;
Message = error.Message;
ValidationErrors = error.ValidationErrors;
}
}
Angular side:
import { IAjaxResponse } from '@abp/abpHttpInterceptor';
import { TokenService } from '@abp/auth/token.service';
import { Component, Injector, ViewChild } from '@angular/core';
import { AppConsts } from '@shared/AppConsts';
import { AppComponentBase } from '@shared/common/app-component-base';
import { ProfileServiceProxy, UpdateProfilePictureInput } from '@shared/service-proxies/service-proxies';
import { FileUploader, FileUploaderOptions } from 'ng2-file-upload';
import { ModalDirective } from 'ngx-bootstrap';
import { finalize } from 'rxjs/operators';
@Component({
selector: 'changeProfilePictureModal',
templateUrl: './change-profile-picture-modal.component.html'
})
export class ChangeProfilePictureModalComponent extends AppComponentBase {
@ViewChild('changeProfilePictureModal') modal: ModalDirective;
public active = false;
public uploader: FileUploader;
public temporaryPictureUrl: string;
public saving = false;
private maxProfilPictureBytesUserFriendlyValue = 5;
private temporaryPictureFileName: string;
private _uploaderOptions: FileUploaderOptions = {};
constructor(
injector: Injector,
private _profileService: ProfileServiceProxy,
private _tokenService: TokenService
) {
super(injector);
}
initializeModal(): void {
this.active = true;
this.temporaryPictureUrl = '';
this.temporaryPictureFileName = '';
this.initFileUploader();
}
show(): void {
this.initializeModal();
this.modal.show();
}
close(): void {
this.active = false;
this.imageChangedEvent = '';
this.uploader.clearQueue();
this.modal.hide();
}
imageChangedEvent: any = '';
fileChangeEvent(event: any): void {
this.imageChangedEvent = event;
}
imageCroppedFile(file: File) {
var files: File[] = [file];
this.uploader.clearQueue();
this.uploader.addToQueue(files);
}
initFileUploader(): void {
this.uploader = new FileUploader({ url: "http://localhost:22742/Profile/UploadProfilePicture' });
this._uploaderOptions.autoUpload = false;
this._uploaderOptions.authToken = 'Bearer ' + this._tokenService.getToken();
this._uploaderOptions.removeAfterUpload = true;
this.uploader.onAfterAddingFile = (file) => {
file.withCredentials = false;
};
this.uploader.onSuccessItem = (item, response, status) => {
const resp = <IAjaxResponse>JSON.parse(response);
if (resp.success) {
this.updateProfilePicture(resp.result.fileName);
} else {
this.message.error(resp.error.message);
}
};
this.uploader.setOptions(this._uploaderOptions);
}
updateProfilePicture(fileName: string): void {
const input = new UpdateProfilePictureInput();
input.fileName = fileName;
input.x = 0;
input.y = 0;
input.width = 0;
input.height = 0;
this.saving = true;
this._profileService.updateProfilePicture(input)
.pipe(finalize(() => { this.saving = false; }))
.subscribe(() => {
abp.event.trigger('profilePictureChanged');
this.close();
});
}
save(): void {
this.uploader.uploadAll();
}
}
Note: Don't forget to add bearer token to your ajax upload. Beside, you need to grant write permission to the physical upload directory in your IIS on production.
Upvotes: 1