I am trying to upload files using aspnet core using ajax request .
In previous versions of .net i used to handle this using
foreach (string fileName in Request.Files)
{
HttpPostedFileBase file = Request.Files[fileName];
//Save file content goes here
fName = file.FileName;
(...)
but now its showing error at request.files how can i get it to work ? i searched and found that httppostedfile has been changed to iformfile but how to handle request.files?
This is working code from a recent project. Data has been moved from Request.Files to Request.Form.Files.
In case you need to convert stream to byte array - this is the only implementation that worked for me. Others would return empty array.
using System.IO;
var filePath = Path.GetTempFileName();
foreach (var formFile in Request.Form.Files)
{
if (formFile.Length > 0)
{
using (var inputStream = new FileStream(filePath, FileMode.Create))
{
// read file to stream
await formFile.CopyToAsync(inputStream);
// stream to byte array
byte[] array = new byte[inputStream.Length];
inputStream.Seek(0, SeekOrigin.Begin);
inputStream.Read(array, 0, array.Length);
// get file name
string fName = formFile.FileName;
}
}
}
This code works for 100% for both files uploaded using regular form or ajax:
[HttpPost]
public async Task<IActionResult> Upload(IList<IFormFile> files)
{
foreach (IFormFile source in files)
{
string filename = ContentDispositionHeaderValue.Parse(source.ContentDisposition).FileName.Trim('"');
filename = this.EnsureCorrectFilename(filename);
using (FileStream output = System.IO.File.Create(this.GetPathAndFilename(filename)))
await source.CopyToAsync(output);
}
return this.RedirectToAction("Index");
}
private string EnsureCorrectFilename(string filename)
{
if (filename.Contains("\\"))
filename = filename.Substring(filename.LastIndexOf("\\") + 1);
return filename;
}
private string GetPathAndFilename(string filename)
{
return this.HostingEnvironment.WebRootPath + "\\files\\" + filename;
}
What about this merge from two good solutions I came around :
var myBytes = await GetByteArrayFromImageAsync(Request.Form.Files[0]);
private async Task<byte[]> GetByteArrayFromImageAsync(IFormFile file)
{
using (var target = new MemoryStream())
{
await file.CopyToAsync(target);
return target.ToArray();
}
}
Related
I am new to API/Rest endpoints, so please forgive my inexperience on this topic.
I am using .net core 3.1. And i have been tasked to write an endpoint, that excepts two parameters a string and a file..
The file will be of binary data (a .bci file format, which i assume is a custom one, but all it is basically is a .txt file, that has been changed to .bci for a machine)
I need to get the file and then read the file using a stringReader and saved to a file locally. Again i am new to endpoints and reading binary data, can anyone help? i have been looking all over the internet today but with no prevail.
I know the below code is incorrect, but really struggling on this one. Any help would much be appreciated.
//GET: api/ProcessResultsFiles]
[HttpGet]
public async Task<IActionResult> ProcessResults(IFormFile file, string bench)
{
await ReadData(file);
return Ok();
}
private static Task<byte[]> ReadData(IFormFile benchNameFile)
{
using (StringReader sr = new StringReader(benchNameFile))
{
string input = null;
while ((input = sr.ReadLine()) != null)
{
Console.WriteLine(input);
}
}
}
From your description, I assume you want to upload the file to the Physical storage/folder, after that might be you want to download the file to local, if that is the case, you could refer the following sample:
[HttpPost("upload")]
public IActionResult Upload(List<IFormFile> formFiles, string subDirectory)
{
try
{
subDirectory = subDirectory ?? string.Empty;
var target = Path.Combine(_environment.WebRootPath, subDirectory);
if(!Directory.Exists(target))
Directory.CreateDirectory(target);
formFiles.ForEach(async file =>
{
if (file.Length <= 0) return;
var filePath = Path.Combine(target, file.FileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
});
return Ok("Upload success!");
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
[HttpPost("download")]
public IActionResult DownLoad( string subDirectory, string filename)
{
//Build the File Path.
string path = Path.Combine(_environment.WebRootPath, subDirectory +"/"+ filename);
if (System.IO.File.Exists(path))
{
//Read the File data into Byte Array.
byte[] bytes = System.IO.File.ReadAllBytes(path);
//download the file.
return File(bytes, "application/octet-stream", filename);
}
else
{
return Ok("file not exist");
}
}
The result as below:
More detail information about upload file in asp.net core, you can refer the following articles:
Upload files in ASP.NET Core
Upload And Download Multiple Files Using Web API
From the above articles, when upload file, if you want to save the byte array, you can refer the following code:
public async Task<IActionResult> OnPostUploadAsync()
{
using (var memoryStream = new MemoryStream())
{
await FileUpload.FormFile.CopyToAsync(memoryStream);
// Upload the file if less than 2 MB
if (memoryStream.Length < 2097152)
{
var file = new AppFile()
{
Content = memoryStream.ToArray()
};
_dbContext.File.Add(file);
await _dbContext.SaveChangesAsync();
}
else
{
ModelState.AddModelError("File", "The file is too large.");
}
}
return Page();
}
I am trying to implement a "Download All" button that will zip up a selection of files from the server and return them as a zip file download. With the code below, I have the zip file being created. The expected files are inside, with the filenames expected, but the contents of the zipped files appears to be corrupted.
public ActionResult DownloadAll(Guid id)
{
var assets = db.InviteAssets.Include(i => i.AssetPages).Where(w => w.InviteID == id).ToList();
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = "allAssets.zip",
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach (var asset in assets)
{
string path, extension, name;
if (asset.AssetType != AssetType.PDF)
{
path = asset.AssetPages.First(f => f.PageNumber == 1).FilePath;
}
else
{
path = string.Format("/Content/Assets/asset_{0}.pdf", asset.ID);
}
extension = path.Substring(path.IndexOf('.'));
name = "asset" + asset.Order + extension;
var file = archive.CreateEntry(name);
using (var streamWriter = new StreamWriter(file.Open()))
{
using (var fileStream = System.IO.File.Open(Server.MapPath("~" + path), FileMode.Open))
{
int filelength = (int)fileStream.Length;
var filedata = new byte[fileStream.Length];
streamWriter.Write(fileStream.Read(filedata, 0, filelength));
}
}
}
}
return File(memoryStream.ToArray(), "application/json", "allAssets.zip");
}
}
I'm thinking my issue is therefore with this section:
using (var streamWriter = new StreamWriter(file.Open()))
{
using (var fileStream = System.IO.File.Open(Server.MapPath("~" + path), FileMode.Open))
{
int filelength = (int)fileStream.Length;
var filedata = new byte[fileStream.Length];
streamWriter.Write(fileStream.Read(filedata, 0, filelength));
}
}
I keep reading examples that use a method archive.CreateEntryFromFile(filePath, fileName) but no such method is recognised. Has this been deprecated, or requires a higher version of .Net Framework?
Thanks in advance.
The problem is here:
streamWriter.Write(fileStream.Read(filedata, 0, filelength));
You’re reading the file contents into filedata but you’re at the same time writing the return value of Read into the archive, meaning a single int. You need to read and write separately:
fileStream.Read(filedata, 0, filelength));
streamWriter.Write(filedata, 0, filelength);
Or you can use the CreateEntryFromFile extension method in System.IO.Compression.ZipFileExtensions namespace.
I discovered that the reason I couldn't see the CreateEntryFromFile method was because I had not included a reference to System.IO.Compression.FileSystem. Once I added that, I could use CreateEntryFromFile which worked fine.
So now I have: archive.CreateEntryFromFile(Server.MapPath("~" + path), name);
Instead of:
var file = archive.CreateEntry(name);
using (var streamWriter = new StreamWriter(file.Open()))
{
using (var fileStream = System.IO.File.Open(Server.MapPath("~" + path), FileMode.Open))
{
int filelength = (int)fileStream.Length;
var filedata = new byte[fileStream.Length];
fileStream.Read(filedata, 0, filelength);
streamWriter.Write(filedata);
}
}
I'm developing an ASP.NET MVC 5 application, and I wrote a code that allows me to download files stored in a SQL Server database as varbinary, I'm able to download a single file with this:
public JsonResult PrepareSingleFile(int [] IdArray)
{
ImageContext _contexte = new ImageContext();
var response =_contexte.contents.Find(IdArray.FirstOrDefault());
//byte[] FileData =
Encoding.UTF8.GetBytes(response.image.ToString());
byte[] FileData = response.image;
Session["data"] = FileData;
Session["filename"] = response.FileName;
return Json(response.FileName);
}
public FileResult DownloadSingleFile()
{
var fname = Session["filename"];
var data = (byte[]) Session["data"];
//return File(data,"application/pdf");
return File(data,System.Net.Mime.MediaTypeNames.Application.Pdf, fname.ToString()+".pdf");
}
But now I want to download multiple files, so I'm getting the data of each file as a byte array and putting those byte arrays inside a List<byte[]> and I want to download those files as a zip file, so how can I do that?
I tried this:
File(data,"the Mime Type", "file name.extension")
But it doesn't work when data is a List<byte[]>.
You can do that using ZipArchive class available in .NET framework 4.5.
You may add a method in your controller that accepts a List<byte[]> parameter and then converts each byte[] to a memory stream and puts it in a zip file like this one,
public FileResult DownloadMultipleFiles(List<byte[]> byteArrayList)
{
using (MemoryStream ms = new MemoryStream())
{
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
foreach(var file in byteArrayList)
{
var entry = archive.CreateEntry(file.fileName +".pdf", CompressionLevel.Fastest);
using (var zipStream = entry.Open())
{
zipStream.Write(file, 0, file.Length);
}
}
}
return File(ms.ToArray(), "application/zip", "Archive.zip");
}
}
Here is my code:
public async Task<IActionResult> Index(ICollection<IFormFile> files)
{
foreach (var file in files)
uploaddb(file);
var uploads = Path.Combine(_environment.WebRootPath, "uploads");
foreach (var file in files)
{
if (file.Length > 0)
{
var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
await file.SaveAsAsync(Path.Combine(uploads, fileName));
}
}
}
Now I am converting this file into byte array using this code:
var filepath = Path.Combine(_environment.WebRootPath, "uploads/Book1.xlsx");
byte[] fileBytes = System.IO.File.ReadAllBytes(filepath);
string s = Convert.ToBase64String(fileBytes);
And then I am uploading this code into my nosql database.This is all working fine but the problem is i don't want to save the file. Instead of that i want to directly upload the file into my database. And it can be possible if i can just convert the file into byte array directly without saving it.
public async Task<IActionResult> Index(ICollection<IFormFile> files)
{
foreach (var file in files)
uploaddb(file);
var uploads = Path.Combine(_environment.WebRootPath, "uploads");
foreach (var file in files)
{
if (file.Length > 0)
{
var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
///Code to Convert the file into byte array
}
As opposed to saving the data as a string (which allocates more memory than needed and might not work if the binary data has null bytes in it), I would recommend an approach more like
foreach (var file in files)
{
if (file.Length > 0)
{
using (var ms = new MemoryStream())
{
file.CopyTo(ms);
var fileBytes = ms.ToArray();
string s = Convert.ToBase64String(fileBytes);
// act on the Base64 data
}
}
}
Also, for the benefit of others, the source code for IFormFile can be found on GitHub
You can just write a simple extension:
public static class FormFileExtensions
{
public static async Task<byte[]> GetBytes(this IFormFile formFile)
{
await using var memoryStream = new MemoryStream();
await formFile.CopyToAsync(memoryStream);
return memoryStream.ToArray();
}
}
Usage
var bytes = await formFile.GetBytes();
var hexString = Convert.ToBase64String(bytes);
You can use the following code to convert it to a byte array:
foreach (var file in files)
{
if (file.Length > 0)
{
var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
using (var reader = new StreamReader(file.OpenReadStream()))
{
string contentAsString = reader.ReadToEnd();
byte[] bytes = new byte[contentAsString.Length * sizeof(char)];
System.Buffer.BlockCopy(contentAsString.ToCharArray(), 0, bytes, 0, bytes.Length);
}
}
}
You can retrieve your file by using the Request.Form already implemented (as image for instance) :
var bytes = new byte[Request.Form.Files.First().Length];
var hexString = Convert.ToBase64String(bytes);
Hope it help
I've had it suggested to me that I should use FileResult to allow users to download files from my Asp.Net MVC application. But the only examples of this I can find always has to do with image files (specifying content type image/jpeg).
But what if I can't know the file type? I want users to be able to download pretty much any file from the filearea of my site.
I had read one method of doing this (see a previous post for the code), that actually works fine, except for one thing: the name of the file that comes up in the Save As dialog is concatenated from the file path with underscores (folder_folder_file.ext). Also, it seems people think I should return a FileResult instead of using this custom class that I had found BinaryContentResult.
Anyone know the "correct" way of doing such a download in MVC?
EDIT:
I got the answer (below), but just thought I should post the full working code if someone else is interested:
public ActionResult Download(string filePath, string fileName)
{
string fullName = Path.Combine(GetBaseDir(), filePath, fileName);
byte[] fileBytes = GetFile(fullName);
return File(
fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
byte[] GetFile(string s)
{
System.IO.FileStream fs = System.IO.File.OpenRead(s);
byte[] data = new byte[fs.Length];
int br = fs.Read(data, 0, data.Length);
if (br != fs.Length)
throw new System.IO.IOException(s);
return data;
}
You can just specify the generic octet-stream MIME type:
public FileResult Download()
{
byte[] fileBytes = System.IO.File.ReadAllBytes(#"c:\folder\myfile.ext");
string fileName = "myfile.ext";
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
The MVC framework supports this natively. The System.Web.MVC.Controller.File controller provides methods to return a file by name/stream/array.
For example using a virtual path to the file you could do the following.
return File(virtualFilePath, System.Net.Mime.MediaTypeNames.Application.Octet, Path.GetFileName(virtualFilePath));
If you're using .NET Framework 4.5 then you use use the MimeMapping.GetMimeMapping(string FileName) to get the MIME-Type for your file. This is how I've used it in my action.
return File(Path.Combine(#"c:\path", fileFromDB.FileNameOnDisk), MimeMapping.GetMimeMapping(fileFromDB.FileName), fileFromDB.FileName);
Phil Haack has a nice article where he created a Custom File Download Action Result class. You only need to specify the virtual path of the file and the name to be saved as.
I used it once and here's my code.
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Download(int fileID)
{
Data.LinqToSql.File file = _fileService.GetByID(fileID);
return new DownloadResult { VirtualPath = GetVirtualPath(file.Path),
FileDownloadName = file.Name };
}
In my example i was storing the physical path of the files so i used this helper method -that i found somewhere i can't remember- to convert it to a virtual path
private string GetVirtualPath(string physicalPath)
{
string rootpath = Server.MapPath("~/");
physicalPath = physicalPath.Replace(rootpath, "");
physicalPath = physicalPath.Replace("\\", "/");
return "~/" + physicalPath;
}
Here's the full class as taken from Phill Haack's article
public class DownloadResult : ActionResult {
public DownloadResult() {}
public DownloadResult(string virtualPath) {
this.VirtualPath = virtualPath;
}
public string VirtualPath {
get;
set;
}
public string FileDownloadName {
get;
set;
}
public override void ExecuteResult(ControllerContext context) {
if (!String.IsNullOrEmpty(FileDownloadName)) {
context.HttpContext.Response.AddHeader("content-disposition",
"attachment; filename=" + this.FileDownloadName)
}
string filePath = context.HttpContext.Server.MapPath(this.VirtualPath);
context.HttpContext.Response.TransmitFile(filePath);
}
}
Thanks to Ian Henry!
In case if you need to get file from MS SQL Server here is the solution.
public FileResult DownloadDocument(string id)
{
if (!string.IsNullOrEmpty(id))
{
try
{
var fileId = Guid.Parse(id);
var myFile = AppModel.MyFiles.SingleOrDefault(x => x.Id == fileId);
if (myFile != null)
{
byte[] fileBytes = myFile.FileData;
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, myFile.FileName);
}
}
catch
{
}
}
return null;
}
Where AppModel is EntityFramework model and MyFiles presents table in your database.
FileData is varbinary(MAX) in MyFiles table.
its simple just give your physical path in directoryPath with file name
public FilePathResult GetFileFromDisk(string fileName)
{
return File(directoryPath, "multipart/form-data", fileName);
}
public ActionResult Download()
{
var document = //Obtain document from database context
var cd = new System.Net.Mime.ContentDisposition
{
FileName = document.FileName,
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(document.Data, document.ContentType);
}
if (string.IsNullOrWhiteSpace(fileName))
return Content("filename not present");
var path = Path.Combine(your path, your filename);
var stream = new FileStream(path, FileMode.Open);
return File(stream, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
GetFile should be closing the file (or opening it within a using). Then you can delete the file after conversion to bytes-- the download will be done on that byte buffer.
byte[] GetFile(string s)
{
byte[] data;
using (System.IO.FileStream fs = System.IO.File.OpenRead(s))
{
data = new byte[fs.Length];
int br = fs.Read(data, 0, data.Length);
if (br != fs.Length)
throw new System.IO.IOException(s);
}
return data;
}
So in your download method...
byte[] fileBytes = GetFile(file);
// delete the file after conversion to bytes
System.IO.File.Delete(file);
// have the file download dialog only display the base name of the file return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, Path.GetFileName(file));