I have angular project as my client side and .Net 6 web api project as my backend. I am still new to both technologies. I am creating a website and there is a functionality that I am trying to add and haven't been successful so far. I want to upload images into a .Net web api project images folder using angular. I also want to later access those images from angular project. I want to store the path of the image files in the database. I have tried to check for the code on the internet without success. Your assistance will be appreciated.
First, submit your's files from frontend with FormData
postWithFile(url: string, obj: any, files: File[]) {
let cloneHeader: any = {};
let options: any = {
headers: new HttpHeaders(cloneHeader),
observe: 'response',
responseType: 'json'
};
let formData: FormData = new FormData();
if (typeof obj == 'object') { // obj is external submit data
formData.append('data', JSON.stringify(obj));
} else {
formData.append('data', obj);
}
if (files && files.length > 0) {
files.forEach((ds, index) => {
formData.append('file_' + index, ds, ds.name);
});
}
return this._http
.post(this.host + url, formData, options)
.pipe(map((res: any) => {
return res.body;
}));
}
And backend handle request with HttpContext.Current.Request.Files, save images to server and store path of images in database
[HttpPost]
public ResponseMessage<bool?> UploadImages()
{
var response = new ResponseMessage<bool?>();
try
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
ExternalDataModel model = MessageConvert.DeserializeObject<ExternalDataModel>(HttpContext.Current.Request["data"]); // obj in frontend
//
List<string> listImages = new List<string>();
DateTime now = DateTime.Now;
string buildPath = $"{string.Format("{0:00}", now.Year)}\\{string.Format("{0:00}", now.Month)}\\{string.Format("{0:00}", now.Day)}"; // change by your's folder path
foreach (string file in HttpContext.Current.Request.Files)
{
var fileContent = HttpContext.Current.Request.Files[file];
int fileLength = fileContent.ContentLength;
if (fileContent != null && fileLength > 0)
{
var stream = fileContent.InputStream;
byte[] imgByteArray;
using (MemoryStream memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
imgByteArray = memoryStream.ToArray();
}
string fileName = $"format_file_name_if_need_{fileContent.FileName}";
string RelativeFolder = $"{buildPath}";
string AbsoluteFolder = Path.Combine("FOLDER_IN_SERVER_FULL_PATH", RelativeFolder);
if (!Directory.Exists(AbsoluteFolder))
{
Directory.CreateDirectory(AbsoluteFolder);
}
string pathSave = Path.Combine(RelativeFolder, fileName);
FileHelper.SaveFileFromBinaryArray(pathSave, imgByteArray);
listImages.Add(pathSave);
}
}
// model.listImages = listImages; // assign to model to save to DB
//
// var data = _bus.uploadImage(model);
// if (data)
// {
// response.Data = true;
// response.MessageCode = MessageCodes.UpdateSuccessfully;
// }
}
catch (Exception ex)
{
response.MessageCode = ex.Message;
}
return response;
}
Related
I'm building a REST API application where I want to give an option to users they can upload single or multiple files, I checked with uploading files from Postman but in my controller, I always get zero files, Please help me to know what wrong I'm doing
Postman Image
Below is my code.
public async Task<IActionResult> UploadInvoice([FromForm(Name = "Files")] List <IFormFile> uploadFiles) {
try {
long size = uploadFiles.Sum(f => f.Length);
if (uploadFiles == null || uploadFiles.Count <= 0) {
Response.StatusCode = (int) HttpStatusCode.BadRequest;
return Content(JsonConvert.SerializeObject(new {
message = "No files found."
}), "application/json");
}
var filesToUpload = new List < FileUploadModel > ();
foreach(var file in uploadFiles) {
var fileGuid = Guid.NewGuid().ToString().Replace("-", "");
var path = Path.Combine(Directory.GetCurrentDirectory(), # "wwwroot\docs", $ "{fileGuid}{Path.GetExtension(file.FileName)}");
using(var fileStream = new FileStream(path, FileMode.Create)) {
await file.CopyToAsync(fileStream);
await fileStream.FlushAsync();
}
}
return Content(JsonConvert.SerializeObject(new {
message = ""
}), "application/json");
} catch (Exception ex) {
Response.StatusCode = (int) HttpStatusCode.InternalServerError;
return Content(JsonConvert.SerializeObject(new {
message = ex.Message
}), "application/json");
}
}
I have to pass potential huge files from an ASP.NET Core middle Server to an ASP.NET backend.
I can’t use the ASP.NET backend web API directly, I have to go over a MVC Controller.
Currently my middle Server gets the file in Chunks (and does some verification), saves it to disk and after it completes it rereads it in chunks to pass it forward.
Is there an easy way to pass the chunks without buffering the file?
I currently got this:
MVC Controler:
[HttpPost]
public ActionResult UploadChunk(IFormFile fileChunk, string chunkMetadata)
{
if (!string.IsNullOrEmpty(chunkMetadata))
{
var metaDataObject = JsonConvert.DeserializeObject<ChunkMetadata>(chunkMetadata);
...
AppendContentToFile(tempFilePath, fileChunk); //writes file with FileMode.Append,
}
}
my upload to back end [Edit]:
public IHttpActionResult FileUpload(string fileUri)
{
try
{
if (Request.Content.IsMimeMultipartContent())
{
var configProvider = Resolve<IApplicationConfigurationProvider>();
var uploadRootDir = configProvider.TemporaryFileUploadPath;
var streamProvider = new MultipartStreamProvider(uploadRootDir);
// If the file is huge and is not split into chunks, the 'ReadAsMultipartAsync' call
// takes as long as full file has been copied
var readResult = Request.Content.ReadAsMultipartAsync(streamProvider).Result;
var fileSvc = Resolve<IFileService>();
string targetFilePath = string.Empty;
foreach (MultipartFileData fileData in streamProvider.FileData)
{
ContentDispositionHeaderValue contentDisposition = fileData.Headers.ContentDisposition;
string fileName = contentDisposition.FileName;
if (!GetFileName(fileName, out var targetFileName))
{
return BadRequest($"ContentDisposition.FileName must match 'file' of URI-query! Actual: {targetFileName}");
}
var rawSourceFileInfo = new FileInfo(targetFileName);
if (contentDisposition.Size.HasValue && contentDisposition.Size.Value > 0)
{
if (!fileSvc.CreateNewFilePath(fileUri, new PathOptions(true), out var validFileFullPath))
{
return BadRequest($"Unable to create physical-path from fileId='{fileUri}'");
}
targetFilePath = validFileFullPath.FullName;
fileSvc.AddChunk(validFileFullPath.FullName, contentDisposition.Size.Value, fileData.LocalFileName);
}
else
{
return BadRequest("File upload must set a valid file-length in ContentDisposition");
}
}
return Ok(targetFilePath);
}
else
{
return BadRequest("File upload must be a 'IsMimeMultipartContent'");
}
}
catch (Exception error)
{
LogError(error, "FileUpload");
return InternalServerError(error);
}
}
Thanks in advance for any help!
[Edit]
my not working call from client to back end:
<script>
$(document).ready(function (e) {
$("#uploadImage").on('change', (function (e) {
// append file input to form data
var fileInput = document.getElementById('uploadImage');
var file = fileInput.files[0];
var formData = new FormData();
formData.append('uploadImage', file);
$.ajax({
url: "http://localhost/service/filestore/v1/upload?fileUri=someText",
type: "POST",
data: formData,
contentType: false,
cache: false,
processData: false,
success: function (data) {
if (data == 'invalid') {
// invalid file format.
$("#err").html("Invalid File !").fadeIn();
}
else {
// view uploaded file.
$("#preview").html(data).fadeIn();
$("#form")[0].reset();
}
},
error: function (e) {
$("#err").html(e).fadeIn();
}
});
}));
});
</script>
I use the code below to send an image to post method and save it as BLOB in DB and it's working successfully:
angular code:
public postUploadedFile(file:any){
this.formData = new FormData();
this.formData.append('file',file,file.name);
this.Url='http://localhost:38300/api/site/PostUploadFiles';
console.log("url passed from here",this.Url)
return this.http.post(this.Url , this.img).subscribe()
}
API code:
public IHttpActionResult PostUploadFiles()
{
int i = 0;
var uploadedFileNames = new List<string>();
string result = string.Empty;
HttpResponseMessage response = new HttpResponseMessage();
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
while(i < httpRequest.Files.Count && result != "Failed")
{
br = new BinaryReader(httpRequest.Files[i].InputStream);
ImageData = br.ReadBytes(httpRequest.Files[i].ContentLength);
br.Close();
if (DB_Operation_Obj.Upload_Image(ImageData) > 0)
{
result = "success";
}
else
{
result = "Failed";
}
i++;
}
}
else
{
result = "can't find images";
}
return Json(result);
}
but now I need to send more info with image ( type id, name) not just the image, so angular code will be like :
public postUploadedFile(file:any, type_id:number,site_id:number){
this.img = new Image_List();
this.img.images = new Array<PreviewURL>();
this.img.type_id= type_id;
this.img.Reference_id = site_id;
this.img.images.push(file);
this.formData = new FormData();
this.formData.append('file',file,file.name);
this.Url='http://localhost:38300/api/site/PostUploadFiles';
console.log("url passed from here",this.Url)
return this.http.post(this.Url , this.img).subscribe()
}
any help to send and insert in DB.
I think you could just make a single upload file method, and make another method for data insert with the file name,so it will be like:
public postUploadedFile(file:any){ this.formData = new FormData(); this.formData.append('file',file,file.name); this.Url='http://localhost:38300/api/site/PostUploadFiles';
This.newMethod(filename);//and here you upload the other data
console.log("url passed from here",this.Url) return this.http.post(this.Url , this.img).subscribe() }
Use FormData to append additional information to api call.
const formData = new FormData();
formData.append(file.name, file,'some-data');
You can use multiple values with the same name.
I have some data to save into a database.
I have created a web api post method to save data. Following is my post method:
[Route("PostRequirementTypeProcessing")]
public IEnumerable<NPAAddRequirementTypeProcessing> PostRequirementTypeProcessing(mdlAddAddRequirementTypeProcessing requTypeProcess)
{
mdlAddAddRequirementTypeProcessing rTyeProcessing = new mdlAddAddRequirementTypeProcessing();
rTyeProcessing.szDescription = requTypeProcess.szDescription;
rTyeProcessing.iRequirementTypeId = requTypeProcess.iRequirementTypeId;
rTyeProcessing.szRequirementNumber = requTypeProcess.szRequirementNumber;
rTyeProcessing.szRequirementIssuer = requTypeProcess.szRequirementIssuer;
rTyeProcessing.szOrganization = requTypeProcess.szOrganization;
rTyeProcessing.dIssuedate = requTypeProcess.dIssuedate;
rTyeProcessing.dExpirydate = requTypeProcess.dExpirydate;
rTyeProcessing.szSignedBy = requTypeProcess.szSignedBy;
rTyeProcessing.szAttachedDocumentNo = requTypeProcess.szAttachedDocumentNo;
if (String.IsNullOrEmpty(rTyeProcessing.szAttachedDocumentNo))
{
}
else
{
UploadFile();
}
rTyeProcessing.szSubject = requTypeProcess.szSubject;
rTyeProcessing.iApplicationDetailsId = requTypeProcess.iApplicationDetailsId;
rTyeProcessing.iEmpId = requTypeProcess.iEmpId;
NPAEntities context = new NPAEntities();
Log.Debug("PostRequirementTypeProcessing Request traced");
var newRTP = context.NPAAddRequirementTypeProcessing(requTypeProcess.szDescription, requTypeProcess.iRequirementTypeId,
requTypeProcess.szRequirementNumber, requTypeProcess.szRequirementIssuer, requTypeProcess.szOrganization,
requTypeProcess.dIssuedate, requTypeProcess.dExpirydate, requTypeProcess.szSignedBy,
requTypeProcess.szAttachedDocumentNo, requTypeProcess.szSubject, requTypeProcess.iApplicationDetailsId,
requTypeProcess.iEmpId);
return newRTP.ToList();
}
There is a field called 'szAttachedDocumentNo' which is a document that's being saved in the database as well.
After saving all data, I want the physical file of the 'szAttachedDocumentNo' to be saved on the server. So i created a method called "UploadFile" as follows:
[HttpPost]
public void UploadFile()
{
if (HttpContext.Current.Request.Files.AllKeys.Any())
{
// Get the uploaded file from the Files collection
var httpPostedFile = HttpContext.Current.Request.Files["UploadedFile"];
if (httpPostedFile != null)
{
// Validate the uploaded image(optional)
string folderPath = HttpContext.Current.Server.MapPath("~/UploadedFiles");
//string folderPath1 = Convert.ToString(ConfigurationManager.AppSettings["DocPath"]);
//Directory not exists then create new directory
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
// Get the complete file path
var fileSavePath = Path.Combine(folderPath, httpPostedFile.FileName);
// Save the uploaded file to "UploadedFiles" folder
httpPostedFile.SaveAs(fileSavePath);
}
}
}
Before running the project, i debbugged the post method, so when it comes to "UploadFile" line, it takes me to its method.
From the file line, it skipped the remaining lines and went to the last line; what means it didn't see any file.
I am able to save everything to the database, just that i didn't see the physical file in the specified location.
Any help would be much appreciated.
Regards,
Somad
Makes sure the request "content-type": "multipart/form-data" is set
[HttpPost()]
public async Task<IHttpActionResult> UploadFile()
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
try
{
MultipartMemoryStreamProvider provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
if (provider.Contents != null && provider.Contents.Count == 0)
{
return BadRequest("No files provided.");
}
foreach (HttpContent file in provider.Contents)
{
string filename = file.Headers.ContentDisposition.FileName.Trim('\"');
byte[] buffer = await file.ReadAsByteArrayAsync();
using (MemoryStream stream = new MemoryStream(buffer))
{
// save the file whereever you want
}
}
return Ok("files Uploded");
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
I've successfully made upload/download calls with the vanilla web api, but now I am trying to consolidate my logic on ODataControllers, so making these upload/download calls part of the same controller that returns the list of files.
To do this I made an action:
[HttpPost]
public Task<IEnumerable<FileComponent>> Upload()
{
try
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var provider = new MultipartFormDataStreamProvider(root);
return Request.Content.ReadAsMultipartAsync(provider).
ContinueWith<IEnumerable<FileComponent>>(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, t.Exception));
}
var fileComponents = new List<FileComponent>();
foreach (MultipartFileData file in provider.FileData)
{
string filename = file.Headers.ContentDisposition.FileName.Replace("\"", "");
var filepath = root + filename;
if (File.Exists(filepath))
{
// If previous entry exists, delete
File.Delete(filepath);
}
// The file is saved with a random GUID for name. Can rename it here.
string guidFilepath = file.LocalFileName;
File.Move(guidFilepath, filepath);
fileComponents.Add(new FileComponent()
{
Name = filename,
Size = new FileInfo(filepath).Length
});
}
return fileComponents;
});
}
finally
{
log.Info("Exiting Post");
}
}
However it never works. It always arrives with no FormData populating in the MultipartFormDataStreamProvider, and the exception is caught and returned as 'Unexpected end of MIME multipart stream. MIME multipart message is not complete.'.
Any ideas? I'm assuming OData is cleaning it.