How to get the last file in an array - c#

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;
}
}

Related

IFormFile copy to memorystream ObjectDisposedException

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);
}
}

Read Text file without copying to hard disk

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();
}

How to return a filestream together with JSON model from ASPNETCore API call?

I have a controller action which is returning a file using the following line of code. However, I need return a filestream with a model which may contain some other JSON serialized data.
I've tried a few different approaches but haven't found a solution which seems to work very well. Has anyone done this before and what is considered the best approach?
this.File(fs, "application/zip", fileName);
Note: The question answered here is referring to multiple FileStreams, whereas I need to return back a FileStream and a Model as JSON.
However, I have modified this solution with the following. What does everyone think?
return new MultipartResult()
{
new MultipartFileContent()
{
ContentType = "application/zip",
FileName = fileName,
Stream = this.OpenFile(filePath)
},
new MultipartModelContent()
{
ContentType = "application/json",
Model = model
}
};
public async Task ExecuteResultAsync(ActionContext context)
{
foreach (var item in this.OfType<MultipartFileContent>())
{
if (item.Stream != null)
{
var c = new StreamContent(item.Stream);
if (item.ContentType != null)
{
c.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(item.ContentType);
}
if (item.FileName != null)
{
var contentDisposition = new ContentDispositionHeaderValue("attachment");
contentDisposition.SetHttpFileName(item.FileName);
c.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
c.Headers.ContentDisposition.FileName = contentDisposition.FileName.Value;
c.Headers.ContentDisposition.FileNameStar = contentDisposition.FileNameStar.Value;
}
this.content.Add(c);
}
}
foreach (var item in this.OfType<MultipartModelContent>())
{
var c = new ObjectContent<object>(item, new JsonMediaTypeFormatter(), item.ContentType);
if (item.ContentType != null)
{
c.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(item.ContentType);
}
this.content.Add(c);
}
context.HttpContext.Response.ContentLength = content.Headers.ContentLength;
context.HttpContext.Response.ContentType = content.Headers.ContentType.ToString();
await content.CopyToAsync(context.HttpContext.Response.Body);
}

return file application.pdf to browser via asp net mvc controller shows code instead of file

I have a pdf file in a database that I loaded in the controller and directly want to return via return File
return File(document.Data, document.ContentType);
Data is the bytearray with ~76000 bytes
ContentType is the application/pdf
When I want to view the result in webbrowser (either FF or Chrome) I get to see the pdf code instead of the pdf.
%PDF-1.4 %���� 1 0 obj <>stream x��� and so on
Would appreciate any help because it must be so simple, but I can't find it.
The return is placed in ActionResult Index and I need it to be loaded right at click on page (no new page with _blank)
It is the right data, because when I use f12 in chrome and click on network and the data I get to view the pdf as a whole
Edit1:
[HttpGet]
public ActionResult Index(int Id)
{
InitMvcApplicationMenu();
...
var document = WebApi.LoadDocument(DocumentGuid.Value);
var byteArray = document.Data;
if (byteArray == null)
{
return null;
}
Stream stream = new MemoryStream(byteArray);
if (document == null)
{
return View("NoDocument");
}
Response.AppendHeader("content-disposition", "inline; filename=file.pdf");
return File(stream, document.ContentType, "document.pdf");
}
This way I get an error no file was found. When I use it the way before with
return File(document.Data, document.ContentType);
I get the bytearray as view instead of a pdf, but the file is found
Edit 2:
public ActionResult Index(int Id)
{
InitMvcApplicationMenu();
var entity = WebApi.LoadItem(Id);
var DocumentGuid = entity.ReportDocumentGUID;
if (DocumentGuid == Guid.Empty)
{
return View("NoDocument");
}
var document = WebApi.LoadItem(DocumentGuid.Value);
if (document == null)
{
return View("NoStatusReportDocument");
}
var cd = new ContentDisposition
{
FileName = document.Name,
Inline = true
};
Response.Headers.Add("Content-Disposition", cd.ToString());
return File(document.Data, document.ContentType);
}
I have a Wrapper with multiple registertabs and want to show the pdf inside the tab when the document tab is selected.
This happens here:
my.onHashChanged = function (e) {
var feature = jHash.val('feature');
my.loadTab(feature);
}
my.loadTab = function (feature) {
if (feature) {
$("#tabstrip-content").html("Loading...");
applicationUIModule.loadPartialView(feature, "Index", { Id: $("#Id").val()}
, function (data) {
}
, null
, $("#tabstrip-content")
);
}
}
my.onTabSelect = function (e) {
var feature = $(e.item).data("feature");
applicationUIModule.updateHash("feature", feature);
}
This is what you have to do:
[HttpGet]
public IActionResult document(int id)
{
var byteArray = db.instances.Where(c => c.id == id).FirstOrDefault().document;
if (byteArray == null)
{
return null;
}
Stream stream = new MemoryStream(byteArray);
Response.AppendHeader("content-disposition", "inline; filename=file.pdf");
return File(stream, "application/pdf", "document.pdf");
}

How to display uploaded image in asp .net application?

I uploaded an image to server using form fileData:
[Route("upload")]
[HttpPost]
public async Task<HttpResponseMessage> Upload()
{
try
{
if (!Request.Content.IsMimeMultipartContent()) {
Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
var provider = GetMultipartProvider();
var result = await Request.Content.ReadAsMultipartAsync(provider);
//Get Album name from Form
var titleOfAlbum = GetTitleOfAlbum(provider);
//get path to file
var pathToCoverDecoded = result.FileData.First().LocalFileName;
//Encodeing to base 64 path
var bytes = Encoding.UTF8.GetBytes(pathToCoverDecoded);
var base64 = Convert.ToBase64String(bytes);
Album al = new Album();
al.Title = titleOfAlbum;
al.PathToCover = base64;
db.Albums.Add(al);
db.SaveChanges();
return new HttpResponseMessage(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
private string GetDesereleazedFileName(MultipartFileData fileData)
{
var fileName = GetFileName(fileData);
return JsonConvert.DeserializeObject(fileName).ToString();
}
private string GetFileName(MultipartFileData fileData)
{
return fileData.Headers.ContentDisposition.FileName;
}
private MultipartFormDataStreamProvider GetMultipartProvider()
{
var uploadFolder = HttpContext.Current.Server.MapPath("~/Files");
if (Directory.Exists(uploadFolder) == false)
{
Directory.CreateDirectory(uploadFolder);
}
return new MultipartFormDataStreamProvider(uploadFolder);
}
private string GetTitleOfAlbum(MultipartFormDataStreamProvider provider)
{
var titleOfAlbum = "";
foreach(var key in provider.FormData.GetValues(0))
{
titleOfAlbum = key;
}
return titleOfAlbum;
}
}
Path looks like:
"C:\Users\Oops\Documents\Visual Studio 2015\Projects\WebApplication1\ForMyCustomers\WebApplication1\Files\BodyPart_b40d80c5-47dc-41db-8e35-9d39d4e27939"
I getting path from FileData:
and convert it to base64, but it doesn't displays at page
I've got File not found error.
How can I resolve it? if the URL is wrong how can I get correct one?
You cannot use physical path (the one you used) on web. The physical path like "C:\something" is the path that can be used only by your OS.
The URL however, is the path that you need and to use and to do that you need to put your files somewhere that is readable by your host (IIS).
You are already writing your files in "~/Files". so you just need to add the file name at the end.
var url= "~/Files/"+filename;
you need to save the file name when you are uploading your file so when you want to fetch data from DB, fetch the file name from DB and create the url using that.

Categories

Resources