I'm currently using tinyMCE to create some news post for a website.
I need to be able to upload images, however i've hit a stopblock.
When I hit my controller I get an
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'FileBufferingReadStream'.
at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ThrowIfDisposed()
at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.set_Position(Int64 value)
at Microsoft.AspNetCore.Http.Internal.ReferenceReadStream..ctor(Stream inner, Int64 offset, Int64 length)
at Microsoft.AspNetCore.Http.Internal.FormFile.OpenReadStream()
at Microsoft.AspNetCore.Http.Internal.FormFile.CopyToAsync(Stream target, CancellationToken cancellationToken)
at HardwareOnlineDk.Web.Areas.Admin.Controllers.ImageController.Upload(IFormFile inputFile) in D:\Kode\HardwareOnlineRider\HOL\SourceCode\Main\Web\Areas\Admin\Controllers\ImageController.cs:line 139
My code looks like this:
[HttpPost, ActionName("Upload")]
public async Task<IActionResult> Upload(IFormFile inputFile)
{
try
{
var filesCount = Request.Form.Files.Count;
if (filesCount == 0)
return BadRequest("Ingen fil fundet");
// Get HTTP posted file based on the fieldname.
var file = Request.Form.Files.GetFile("file");
if (file == null)
return BadRequest("Fejlkonfiguration: Filnavn ikke korrekt");
// Check if the file is valid.
if (!Check(file.FileName, file.ContentType))
return BadRequest("Fil ikke gyldig");
var memoryStream = new MemoryStream();
await file.CopyToAsync(memoryStream);
var medie = new Medie
{
Name = file.FileName.Trim('\"'),
ParentId = _imageService.TempFolderGuid,
ContentLength = file.Length,
Content = memoryStream.ToArray()
};
try
{
var imageId = await _imageService.Medier_InsertMedie(medie);
//TODO Her skal vi gemme ImageId i Session
return Json(new
{
location = $"/api/media/{imageId.Id}.jpg"
});
}
catch
{
return BadRequest("Kunne ikke gemme billede");
}
}
catch
{
return StatusCode(500);
}
}
And the Check Method if needed
private static bool Check(string filePath, string mimeType)
{
return AllowedImageExts.Contains(GetFileExtension(filePath)) &&
AllowedImageMimetypes.Contains(mimeType.ToLower());
}
The code fails when i'm doing:
await file.CopyToAsync(memoryStream)
Can anyone help me here. I'm lost.
UPDATE 1
I just tried to fix it with the suggested answer, so my code now looks like this:
[HttpPost, ActionName("Upload")]
public async Task<IActionResult> Upload([FromForm]IFormFile file)
{
try
{
if (file == null)
return BadRequest("Fejlkonfiguration: Filnavn ikke korrekt");
var memoryStream = new MemoryStream();
await file.CopyToAsync(memoryStream);
var filesCount = Request.Form.Files.Count;
if (!Request.ContentType.StartsWith(MultipartContentType))
return BadRequest("Contenttype er ikke korrekt");
if (filesCount == 0)
return BadRequest("Ingen fil fundet");
// Get HTTP posted file based on the fieldname.
// Check if the file is valid.
if (!Check(file.FileName, file.ContentType))
return BadRequest("Fil ikke gyldig");
var medie = new Medie
{
Name = file.FileName.Trim('\"'),
ParentId = _imageService.TempFolderGuid,
ContentLength = file.Length,
Content = memoryStream.ToArray()
};
try
{
var imageId = await _imageService.Medier_InsertMedie(medie);
//TODO Her skal vi gemme ImageId i Session
return Json(new
{
location = $"/api/media/{imageId.Id}.jpg"
});
}
catch
{
return BadRequest("Kunne ikke gemme billede");
}
}
catch
{
return StatusCode(500);
}
}
The input parameter is no longer null, but it still throws the same exception
You indicated that inputFile is always null.
Binding matches form files by name.
Reference Upload files in ASP.NET Core
Based on
// Get HTTP posted file based on the fieldname.
var file = Request.Form.Files.GetFile("file");
the name of the posted field is "file"
rename the action parameter to match and explicitly state when to bind the data using [FromForm]
[HttpPost, ActionName("Upload")]
public async Task<IActionResult> Upload([FromForm]IFormFile file) {
try {
if (file == null)
return BadRequest("Fejlkonfiguration: Filnavn ikke korrekt");
// Check if the file is valid.
if (!Check(file.Name, file.ContentType))
return BadRequest("Fil ikke gyldig");
var medie = new Medie {
Name = file.Name.Trim('\"'),
ParentId = _imageService.TempFolderGuid
};
var fileStream = file.OpenStreamRead();
using (var memoryStream = new MemoryStream()) {
await fileStream.CopyToAsync(memoryStream);
medie.Content = memoryStream.ToArray();
medie.ContentLength = memoryStream.Length,
}
try {
var imageId = await _imageService.Medier_InsertMedie(medie);
//TODO Her skal vi gemme ImageId i Session
return Json(new {
location = $"/api/media/{imageId.Id}.jpg"
});
} catch {
return BadRequest("Kunne ikke gemme billede");
}
} catch {
return StatusCode(500);
}
}
I feel like I should post how I solved it
The issue was that I have a log in middleware, that reads the httpcontext.
Using the answer found here: How can I read http request body in netcore 3 more than once?
solved the issue for me.
you can pass additional parameters with a class with a IFormFile element. pass the byte data to a stored procure as a byte[] array. where the FileData field is a varbinary parameter in the stored procedure.
public class FormData
{
public string FileName { get; set; }
public string FileDescription { get; set; }
public IFormFile file { get; set; }
}
controller end point
public async Task<IActionResult> UploadDocument([FromForm] FormData formData)
{
if (formData.file.Length > 0)
{
using (MemoryStream ms = new MemoryStream())
{
await formData.file.CopyToAsync(ms);
data = ms.ToArray();
await _repository.spProcedure(FileData: data);
}
}
Related
I have this code to download a file in the database, but it only downloads the first user upload.
I tried using LastOrDefault and reverse to download the most recent file but I couldn't.
Does anyone have any idea how to do it?
[HttpGet]
[Route("~/DownloadFileCNPJ/{Id}")]
public async Task<IActionResult> DownloadFileCNPJ(long Id)
{
using (var context = new MyDbContext())
{
var cartaoCnpj = (from j in context.CartaoCNPJCedentes
where j.CedenteId.Equals(Id)
select j).Reverse().FirstOrDefault();
if (cartaoCnpj != null)
{
var content = new System.IO.MemoryStream(cartaoCnpj.CartaoCNPJ);
var contentType = cartaoCnpj.TipoCartãoCNPJ;
var fileName = cartaoCnpj.NomeArquivoCNPJ;
return File(content, contentType, fileName);
}
return null;
}
}
In Chrome i get following error:
Not allowed to load local resource: C://...
Now i want to change the return from the Image in like 'localhost:44346/wwwroot/images/profileimages/img.jpg'
Can you tell my how i can do this?
This is my Controller for the Fileupload:
[HttpPut]
[Authorize]
[RequestSizeLimit(1_000_000)]
[Route("UpdateImage")]
public async Task<IActionResult> UpdateImage([FromForm]ApplicationUserModel model)
{
try
{
var user = await _userManager.FindByIdAsync(model.id);
if (user.ProfileImagePath != null)
{
var file = new FileInfo(user.ProfileImagePath);
file.Delete();
}
var uniqueFileName = GetUniqueFileName(model.ProfileImage.FileName);
var uploads = Path.Combine(hostingEnvironment.WebRootPath, "Images\\ProfileImages");
var filePath = Path.Combine(uploads, uniqueFileName);
await model.ProfileImage.CopyToAsync(new FileStream(filePath, FileMode.Create));
user.ProfileImagePath = filePath;
var result = await _userManager.UpdateAsync(user);
return Ok(result);
}
catch (Exception ex)
{
throw ex;
}
}
This is the Controller for getting the User Informations:
[HttpGet]
[Authorize]
[Route("GetCurrentUser")]
public async Task<IActionResult> GetCurrentUser()
{
try
{
var userId = User.FindFirst("UserID")?.Value;
var data = await _userManager.FindByIdAsync(userId);
var user = new UserStandardModel
{
id = userId,
LastName = data.LastName,
FirstName = data.FirstName,
ProfileImagePath = data.ProfileImagePath
};
return Ok(user);
}
catch (Exception ex)
{
throw ex;
}
}
So i have found a solution for my problem.
When i trigger my GetCurrentUser function than i will check i the FilePath starts with 'C'.
When this is true i will format the filepath with my localhost address. Like Bruno suggested.
But this works only if i have the UseStaticFiles() in my Startup.cs
app.UseStaticFiles();
This is not beautiful but it does the work and its only for testing.
var userId = User.FindFirst("UserID")?.Value;
var data = await _userManager.FindByIdAsync(userId);
var user = new UserStandardModel
{
id = userId,
LastName = data.LastName,
FirstName = data.FirstName,
ProfileImagePath = data.ProfileImagePath
};
if (user.ProfileImagePath.StartsWith('C'))
{
var url = "https://localhost:44356/";
user.ProfileImagePath = user.ProfileImagePath.Remove(0,58);
user.ProfileImagePath = url + user.ProfileImagePath;
}
return Ok(user);
I'm using Asp.Net Core 3.0 and I find myself in a situation where the client will pass text file(s) to my API, the API will then parse the text files into a data model using a function that I have created called ParseDataToModel(), and then store that data model into a database using Entity Framework. Since my code is parsing the files into a data model, I really don't need to copy it to the hard disk if it isn't necessary. I don't have a ton of knowledge when it comes to Streams, and I've googled quite a bit, but I was wondering if there is a way to retrieve the string data of the uploaded files without actually copying them to the hard drive? It seems like a needless extra step.... Below is my code for the file Upload and insertion into the database:
[HttpPost("upload"), DisableRequestSizeLimit]
public IActionResult Upload()
{
var filePaths = new List<string>();
foreach(var formFile in Request.Form.Files)
{
if(formFile.Length > 0)
{
var filePath = Path.GetTempFileName();
filePaths.Add(filePath);
using(var stream = new FileStream(filePath, FileMode.Create))
{
formFile.CopyTo(stream);
}
}
}
BaiFiles lastFile = null;
foreach(string s in filePaths)
{
string contents = System.IO.File.ReadAllText(s);
BaiFiles fileToCreate = ParseFileToModel(contents);
if (fileToCreate == null)
return BadRequest(ModelState);
var file = _fileRepository.GetFiles().Where(t => t.FileId == fileToCreate.FileId).FirstOrDefault();
if (file != null)
{
ModelState.AddModelError("", $"File with id {fileToCreate.FileId} already exists");
return StatusCode(422, ModelState);
}
if (!ModelState.IsValid)
return BadRequest();
if (!_fileRepository.CreateFile(fileToCreate))
{
ModelState.AddModelError("", $"Something went wrong saving file with id {fileToCreate.FileId}");
return StatusCode(500, ModelState);
}
lastFile = fileToCreate;
}
return CreatedAtRoute("GetFile", new { fileId = lastFile.FileId }, lastFile);
}
It would be nice to just hold all of the data in memory instead of copying them to the hard drive, just to turn around and open it to read the text.... I apologize if this isn't possible, or if this question has been asked before. I'm sure it has, and I just wasn't googling the correct keywords. Otherwise, I could be wrong and it is already doing exactly what I want - but System.IO.File.ReadAllText() makes me feel it's being copied to a temp directory somewhere.
After using John's answer below, here is the revised code for anyone interested:
[HttpPost("upload"), DisableRequestSizeLimit]
public IActionResult Upload()
{
var filePaths = new List<string>();
BaiFiles lastFile = null;
foreach (var formFile in Request.Form.Files)
{
if (formFile.Length > 0)
{
using (var stream = formFile.OpenReadStream())
{
using (var sr = new StreamReader(stream))
{
string contents = sr.ReadToEnd();
BaiFiles fileToCreate = ParseFileToModel(contents);
if (fileToCreate == null)
return BadRequest(ModelState);
var file = _fileRepository.GetFiles().Where(t => t.FileId == fileToCreate.FileId).FirstOrDefault();
if (file != null)
{
ModelState.AddModelError("", $"File with id {fileToCreate.FileId} already exists");
return StatusCode(422, ModelState);
}
if (!ModelState.IsValid)
return BadRequest();
if (!_fileRepository.CreateFile(fileToCreate))
{
ModelState.AddModelError("", $"Something went wrong saving file with id {fileToCreate.FileId}");
return StatusCode(500, ModelState);
}
lastFile = fileToCreate;
}
}
}
}
if(lastFile == null)
return NoContent();
else
return CreatedAtRoute("GetFile", new { fileId = lastFile.FileId }, lastFile);
}
System.IO.File.ReadAllText(filePath) is a convenience method. It essentially does this:
string text = null;
using (var stream = FileStream.OpenRead(filePath))
using (var reader = new StreamReader(stream))
{
text = reader.ReadToEnd();
}
FormFile implements an OpenReadStream method, so you can simply use this in place of stream in the above:
string text = null;
using (var stream = formFile.OpenReadStream())
using (var reader = new StreamReader(stream))
{
text = reader.ReadToEnd();
}
I, am using angular 5, with asp.net core 2.0. I, am trying to upload the file to the server. The code update the file to the server. But with 0 kb of data and sometime it upload the file.
The file size is not large. Its in KB.
Here is the Angular code
public QuestionPostHttpCall(_questionPhotoVM: QuestionPhotoViewModel): Observable<GenericResponseObject<QuestionPhotoViewModel[]>> {
const formData: FormData = new FormData();
formData.append('FileUpload', _questionPhotoVM.FileUpload);
formData.append('QuestionText', _questionPhotoVM.questionText);
formData.append('QuestionId', _questionPhotoVM.questionId);
const headers = new HttpHeaders().set('enctype', 'multipart/form-data');
return this._httpClientModule.post<GenericResponseObject<QuestionPhotoViewModel[]>>(this.questionPhotoUrl, formData);
}
In the controller I, can receive the file.
Here is the controller method
[HttpPost]
public JsonResult QuestionPhotoPost(IFormFile FileUpload, string QuestionText, Guid? QuestionId)
{
string TempFileName = string.Empty;
var directiveToUpload = Path.Combine(_environment.WebRootPath, "images\\UploadFile");
var http = HttpRequestExtensions.GetUri(Request);
QuestionViewModel model = new QuestionViewModel();
try
{
if (FileUpload != null)
{
TempFileName = FileUpload.FileName;
CheckFileFromFrontEnd();
}
}
catch (Exception exception)
{
}
void CheckFileFromFrontEnd()
{
if (FileUpload != null)
{
if (!System.IO.Directory.Exists(directiveToUpload))
{
System.IO.Directory.CreateDirectory(directiveToUpload);
}
if (System.IO.File.Exists(string.Format("{0}\\{1}\\{2}", _environment.WebRootPath, "images\\UploadFile", FileUpload.FileName)))
{
TempFileName = Guid.NewGuid().ToString() + FileUpload.FileName;
}
model.PictureUrl = string.Format("{0}://{1}/{2}/{3}/{4}", http.Scheme, http.Authority, "images", "UploadFile", TempFileName);
SaveFileToServer(TempFileName);
}
}
void SaveFileToServer(string FileName)
{
if (FileUpload.Length > 0)
{
using (var stream = new FileStream(Path.Combine(directiveToUpload, FileName), FileMode.Create))
{
FileUpload.CopyToAsync(stream);
}
}
}
return Json(genericResponseObject);
}
The file is uploaded to the server. But some time it upload with 0 byte and sometime it upload correctly.
The resolution of file is 570 X 400 and size of file 197KB
Where I, am doing wrong?? Please anyone let me know. Do, I need to specify max byte in somewhere ??
Your problem is that you are using an asynchronous function and not awaiting it.
You are using ASP.NET Core so you should (read "must") use the async-all-the-way pattern:
[HttpPost]
public async Task<JsonResult> QuestionPhotoPost(IFormFile FileUpload, string QuestionText, Guid? QuestionId)
{
string TempFileName = string.Empty;
var directiveToUpload = Path.Combine(_environment.WebRootPath, "images\\UploadFile");
var http = HttpRequestExtensions.GetUri(Request);
QuestionViewModel model = new QuestionViewModel();
try
{
if (FileUpload != null)
{
TempFileName = FileUpload.FileName;
await CheckFileFromFrontEndAsync();
}
}
catch (Exception exception)
{
}
async Task CheckFileFromFrontEndsync()
{
if (FileUpload != null)
{
if (!System.IO.Directory.Exists(directiveToUpload))
{
System.IO.Directory.CreateDirectory(directiveToUpload);
}
if (System.IO.File.Exists(string.Format("{0}\\{1}\\{2}", _environment.WebRootPath, "images\\UploadFile", FileUpload.FileName)))
{
TempFileName = Guid.NewGuid().ToString() + FileUpload.FileName;
}
model.PictureUrl = string.Format("{0}://{1}/{2}/{3}/{4}", http.Scheme, http.Authority, "images", "UploadFile", TempFileName);
await SaveFileToServerAsync(TempFileName);
}
}
async Task SaveFileToServerAsync(string FileName)
{
if (FileUpload.Length > 0)
{
using (var stream = new FileStream(Path.Combine(directiveToUpload, FileName), FileMode.Create))
{
await FileUpload.CopyToAsync(stream);
}
}
}
return Json(genericResponseObject);
}
To make the code more readable, I'd move those inline functions to outside, though.
I don't know how to solve the following problem:
An object reference is required for the non-static field, method, or property 'HttpContext.Request'
I am using the new Visual Studio 2015 with ASP.NET 5 MVC 6. In that Version the HttpContext.Request is an abstract method and i dont't know how to use it.
namespace STReportGenerator.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult Upload()
{
ViewBag.Title = "scaffmax config";
if (**HttpContext.Request.InputStream.Length** == 0)
{
ViewData["error"] = "No configuration data uploaded";
return RedirectToAction("XMLUpload", "Home");
}
var inputStream = HttpContext.Request.InputStream;
if (HttpContext.Request.Files.Count != 0)
{
inputStream = HttpContext.Request.Files[0].InputStream;
}
DataSet dataSet = new DataSet();
try
{
dataSet.ReadXml(inputStream);
}
catch
{
ViewData["error"] = "The configuration data is invalid";
return RedirectToAction("XMLUpload", "Home");
}
inputStream.Position = 0;
Configuration Config = new Configuration();
Config.LoadFromXMLStream(inputStream);
string g = Guid.NewGuid().ToString();
string configFileName = HttpContext.Server.MapPath("~/xml/") + g + ".xml";
dataSet.WriteXml(configFileName);
switch (Config.Language)
{
case "de":
{
ViewData["headline"] = "Auswahl Reportvorlage";
ViewData["imageContent"] = "Firmenlogo:";
ViewData["projectlist"] = "Wähle eine Vorlage:";
ViewData["sendButton"] = "Senden";
break;
}
case "us":
{
ViewData["headline"] = "Report-Template Selection";
ViewData["imageContent"] = "Company Logo:";
ViewData["projectlist"] = "Select a template:";
ViewData["sendButton"] = "Send";
break;
}
}
ReportList rL = new ReportList(HttpContext.Server.MapPath("~/Reports"));
for (int i = 0; i < rL.reportsList.Count; i++)
{
if (!CheckXmlandXsd(rL.reportsList[i], g))
{
rL.reportsList.RemoveAt(i);
i--;
}
}
if (rL.reportsList.Count == 0)
{
ViewData["errorTemplateList"] = "No valid template on the server. Please try an other xml data.";
}
ViewData["inputXmlName"] = g;
ViewData["Logo"] = Config.Logo;
ViewBag.ShowDesigner = Config.ShowDesigner;
ViewBag.Template = Config.Template;
return View(rL.reportsList);
}
public bool CheckXmlandXsd(string xsdname, string xmlname)
{
try
{
XmlDocument xmld = new XmlDocument();
xmld.Load(HttpContext.Server.MapPath("~/xml/") + xmlname + ".xml");
xmld.Schemas.Add(null, HttpContext.Server.MapPath("~/xsd/") + xsdname + ".xsd");
xmld.Validate(ValidationCallBack);
return true;
}
catch
{
return false;
}
}
private void ValidationCallBack(object sender, System.Xml.Schema.ValidationEventArgs e)
{
throw new NotImplementedException();
}
}
}
You reference the current HTTP Context via the Controllers's Context property:
var request = Context.Request;
However, file uploading has changed in ASP.NET 5. There is no Files collection in the Request property. Uploaded files are available as a collection of IFormFile objects:
[HttpPost]
public async Task<IActionResult> Index(IList<IFormFile> files)
{
foreach (var file in files)
{
var fileName = ContentDispositionHeaderValue
.Parse(file.ContentDisposition)
.FileName
.Trim('"');// FileName returns "fileName.ext"(with double quotes) in beta 3
if (fileName.EndsWith(".txt"))// Important for security if saving in webroot
{
var filePath = _hostingEnvironment.ApplicationBasePath + "\\wwwroot\\"+ fileName;
await file.SaveAsAsync(filePath);
}
}
return RedirectToAction("Index");
}
Check Imran Baloch's blog post where I borrowed the code sample from: https://weblogs.asp.net/imranbaloch/file-upload-in-aspnet5-mvc6