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.
Related
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;
}
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");
}
}
Actually I'm try to upload a file from user. But I'm getting error. I tried various way even the Microsoft doc also. I can't help myself. So please help me
Link: Microsoft Doc dotnet core 3.1
My action :
[HttpPost]
public async Task<IActionResult> Updateperson(UpdatePersonViewModel updatePerson)
{
if (ModelState.IsValid)
{
string uniqueFileName = null;
if(updatePerson.Photo != null)
{
string[] words = updatePerson.Photo.FileName.Split('.');
int a = words.Rank;
uniqueFileName = words[a];
uniqueFileName = Guid.NewGuid().ToString() + "_." + uniqueFileName;
string filePath = Path.Combine("Images",uniqueFileName);
//string filePath = Path.Combine(config["Images"], uniqueFileName);
// using (var stream = System.IO.File.Create(filePath))
// {
// await formFile.CopyToAsync(stream);
// }
await updatePerson.Photo.CopyToAsync(new FileStream(filePath,FileMode.Create));
}
_context.Persons.Update(updatePerson);
_context.SaveChanges();
return RedirectToAction("Profile", new RouteValueDictionary(new { action = "Profile", id = updatePerson.Id }));
}
else
{
return RedirectToAction("Profile", new RouteValueDictionary(new { action = "Profile", id = updatePerson.Id }));
}
}
>>> config is a object of IConfiguration
Here is Error:
It means no such directory named Image exists!
You can simply check if it exists, or create one if it doesn't exist.
if(!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
To test:
Use a directoryPath variable like this:
var directoryPath=Path.Combine(Directory.GetCurrentDirectory(), "Images");
I want to create an attribute which will perform following tasks.
Validate the file type as per the file extension.
Validate the file type as per magic number/signature of file.
Validate the file length/size.
If file is validated then I want to store the file into database as as Base 64 string.
I tried it, but in my solution I have to read file twice first in attribute to check extension, magic number and size and secondly to convert the file stream into base 64 string. But Request.Content is forward only reader so it is throwing error when I tried to read the file again.
Please see the code below
File Validator Filter
public class ValidateFileAttribute : ActionFilterAttribute
{
public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
return Task.Factory.StartNew(async () => {
if (Request.Content.IsMimeMultipartContent())
{
var provider = actionContext.Request.Content.ReadAsMultipartAsync(cancellationToken).Result;
foreach (var content in provider.Contents)
{
//Here logic to check extension, magic number and length.
//If any error occurred then throw exception with HttpStatusCode
var fileName = content.Headers.ContentDisposition == null ? string.Empty : content.Headers.ContentDisposition.FileName;
var fileInBytes = content.ReadAsByteArrayAsync().Result;
var extention = fileName.Substring(fileName.LastIndexOf('.') + 1);
var validExtensions = new List<string>() { "pdf", "doc", "docx" };
if (!validExtensions.Contains(extention, StringComparer.OrdinalIgnoreCase))
{
//Return Exception
}
if (fileInBytes != null && fileInBytes.Any())
{
var magicNumber = BitConverter.ToString(fileInBytes).Substring(0, 11);
var validMagicNumbers = new List<string>() { "25-50-44-46", "D0-CF-11-E0", "50-4B-03-04" };
if (!validMagicNumbers.Contains(magicNumber, StringComparer.OrdinalIgnoreCase))
{
// Return Exception
}
}
if(fileInBytes != null && fileInBytes.Any() && fileInBytes.Length >= 3000000)
{
// Return Exception
}
}
}
}, cancellationToken);
}
}
Upload Action Method
[ValidateFile]
[Route("upload")]
[HttpPost]
public DocumentUploadResponse Upload()
{
if (Request.Content.IsMimeMultipartContent())
{
var provider = Request.Content.ReadAsMultipartAsync().Result;
// Getting error here..
foreach (var content in provider.Contents)
{
//Here logic to convert file stream into base 64 string.
//And store that string into Database.
var fileInBytes = content.ReadAsByteArrayAsync().Result;
var fileToStore = Convert.ToBase64String(fileInBytes);
/// Here goes Database code.....
}
}
}
Your help will be appreciated.
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);
}
}