C# read IFormFile into byte[] - c#

I am trying to read an IFormFile received from a HTTP POST request like this:
public async Task<ActionResult> UploadDocument([FromForm]DataWrapper data)
{
IFormFile file = data.File;
string fileName = file.FileName;
long length = file.Length;
if (length < 0)
return BadRequest();
using FileStream fileStream = new FileStream(fileName, FileMode.OpenOrCreate);
byte[] bytes = new byte[length];
fileStream.Read(bytes, 0, (int)file.Length);
...
}
but something is wrong, after this line executes:
fileStream.Read(bytes, 0, (int)file.Length);
all of the elements of bytes are zero.
Also, the file with the same name is created in my Visual Studio project, which I would prefer not to happen.

You can't open an IFormFile the same way you would a file on disk. You'll have to use IFormFile.OpenReadStream() instead. Docs here
public async Task<ActionResult> UploadDocument([FromForm]DataWrapper data)
{
IFormFile file = data.File;
long length = file.Length;
if (length < 0)
return BadRequest();
using var fileStream = file.OpenReadStream();
byte[] bytes = new byte[length];
fileStream.Read(bytes, 0, (int)file.Length);
}
The reason that fileStream.Read(bytes, 0, (int)file.Length); appears to be empty is, because it is. The IFormFile.Filename is the name of the file given by the request and doesn't exist on disk.

Your code's intent seems to be to write to a FileStream, not a byte buffer. What it actually does though, is create a new empty file and read from it into an already cleared buffer. The uploaded file is never used.
Writing to a file
If you really want to save the file, you can use CopyTo :
using(var stream = File.Create(Path.Combine(folder_I_Really_Want,file.FileName))
{
file.CopyTo(stream);
}
If you want to read from the uploaded file into a buffer without saving to disk, use a MemoryStream. That's just a Stream API buffer over a byte[] buffer. You don't have to specify the size but that reduces reallocations as the internal buffer grows.
Reading into byte[]
Reading into a byte[] through MemoryStream is essentially the same :
var stream = new MemoryStream(file.Length);
file.CopyTo(stream);
var bytes=stream.ToArray();

The problem is that you are opening a new filestream based on the file name in your model which will be the name of the file that the user selected when uploading. Your code will create a new empty file with that name, which is why you are seeing the file in your file system. Your code is then reading the bytes from that file which is empty.
You need to use IFormFile.OpenReadStream method or one of the CopyTo methods to get the actual data from the stream.
You then write that data to your file on your file system with name you want.
var filename ="[Enter or create name for your file here]";
using (FileStream fs = new FileStream(filename, FileMode.OpenOrCreate))
//Create the file in your file system with the name you want.
{
using (MemoryStream ms = new MemoryStream())
{
//Copy the uploaded file data to a memory stream
file.CopyTo(ms);
//Now write the data in the memory stream to the new file
fs.Write(ms.ToArray());
}
}

Related

C# ASP.NET MVC application with file type input on a Mac [duplicate]

I am trying to read an IFormFile received from a HTTP POST request like this:
public async Task<ActionResult> UploadDocument([FromForm]DataWrapper data)
{
IFormFile file = data.File;
string fileName = file.FileName;
long length = file.Length;
if (length < 0)
return BadRequest();
using FileStream fileStream = new FileStream(fileName, FileMode.OpenOrCreate);
byte[] bytes = new byte[length];
fileStream.Read(bytes, 0, (int)file.Length);
...
}
but something is wrong, after this line executes:
fileStream.Read(bytes, 0, (int)file.Length);
all of the elements of bytes are zero.
Also, the file with the same name is created in my Visual Studio project, which I would prefer not to happen.
You can't open an IFormFile the same way you would a file on disk. You'll have to use IFormFile.OpenReadStream() instead. Docs here
public async Task<ActionResult> UploadDocument([FromForm]DataWrapper data)
{
IFormFile file = data.File;
long length = file.Length;
if (length < 0)
return BadRequest();
using var fileStream = file.OpenReadStream();
byte[] bytes = new byte[length];
fileStream.Read(bytes, 0, (int)file.Length);
}
The reason that fileStream.Read(bytes, 0, (int)file.Length); appears to be empty is, because it is. The IFormFile.Filename is the name of the file given by the request and doesn't exist on disk.
Your code's intent seems to be to write to a FileStream, not a byte buffer. What it actually does though, is create a new empty file and read from it into an already cleared buffer. The uploaded file is never used.
Writing to a file
If you really want to save the file, you can use CopyTo :
using(var stream = File.Create(Path.Combine(folder_I_Really_Want,file.FileName))
{
file.CopyTo(stream);
}
If you want to read from the uploaded file into a buffer without saving to disk, use a MemoryStream. That's just a Stream API buffer over a byte[] buffer. You don't have to specify the size but that reduces reallocations as the internal buffer grows.
Reading into byte[]
Reading into a byte[] through MemoryStream is essentially the same :
var stream = new MemoryStream(file.Length);
file.CopyTo(stream);
var bytes=stream.ToArray();
The problem is that you are opening a new filestream based on the file name in your model which will be the name of the file that the user selected when uploading. Your code will create a new empty file with that name, which is why you are seeing the file in your file system. Your code is then reading the bytes from that file which is empty.
You need to use IFormFile.OpenReadStream method or one of the CopyTo methods to get the actual data from the stream.
You then write that data to your file on your file system with name you want.
var filename ="[Enter or create name for your file here]";
using (FileStream fs = new FileStream(filename, FileMode.OpenOrCreate))
//Create the file in your file system with the name you want.
{
using (MemoryStream ms = new MemoryStream())
{
//Copy the uploaded file data to a memory stream
file.CopyTo(ms);
//Now write the data in the memory stream to the new file
fs.Write(ms.ToArray());
}
}

Read docx file from VM and write it down to a stream

I have a .docx file located on my virtual machine desktop which I want to write down to a stream.
So far this is what I have tried
byte[] buffer = new byte[32768];
string path = #"\\officeblrhome.somedomain\UserData$\username\Desktop\filename.docx";
var memoryStr = new MemoryStream();
memoryStr.Write(System.IO.File.ReadAllBytytes(path), 0 , buffer.Length);
using (WordprocessingDocumenet doc = WordprocessingDocument.Open(memoryStr, true)
And I get the an error that the file contains corrupted data. Is it possible that my path is wrong? If it is how to get the valid path from the VM? The word file itself is not corrupted.
I made few changes to the code and seems that it's working
byte[] buffer = System.IO.File.ReadAllBytytes(path);
stream.Flush();
stream.Position = 0;

Inconsistent file size change while writing bytes from stream to a file

I have a file with size 10124, I am adding a byte array, which has length 4 in the beginning of the file.
After that the file size should become 10128, but as I write it to file, the size decreased to 22 bytes. I don't know where is the problem
public void AppendAllBytes(string path, byte[] bytes)
{
var encryptedFile = new FileStream(path, FileMode.Open, FileAccess.Read);
////argument-checking here.
Stream header = new MemoryStream(bytes);
var result = new MemoryStream();
header.CopyTo(result);
encryptedFile.CopyTo(result);
using (var writer = new StreamWriter(#"C:\\Users\\life.monkey\\Desktop\\B\\New folder (2)\\aaaaaaaaaaaaaaaaaaaaaaaaaaa.docx.aef"))
{
writer.Write(result);
}
}
How can I write bytes to the file?
The issue seems to be caused by:
using a StreamWriter to write binary formatted data. The name does not inthuitively suggest this, but the StreamWriter class is suited for writing textual data.
passing an entire stream instead of the actual binary data. To obtain the bytes stored in a MemoryStream, use its convenient ToArray() method.
I suggest you the following code:
public void AppendAllBytes(string path, byte[] bytes)
{
var fileName = #"C:\\Users\\life.monkey\\Desktop\\B\\New folder (2)\\aaaaaaaaaaaaaaaaaaaaaaaaaaa.docx.aef";
using (var encryptedFile = new FileStream(path, FileMode.Open, FileAccess.Read))
using (var writer = new BinaryWriter(File.Open(fileName, FileMode.Append)))
using (var result = new MemoryStream())
{
encryptedFile.CopyTo(result);
result.Flush(); // ensure header is entirely written.
// write header directly, no need to put it in a memory stream
writer.Write(bytes);
writer.Flush(); // ensure the header is written to the result stream.
writer.Write(result.ToArray());
writer.Flush(); // ensure the encryptdFile is written to the result stream.
}
}
The code above uses the BinaryWriter class which is better suited for binary data. It has a Write(byte[] bytes) method overload that is used above to write an entire array to the file. The code uses regular calls to the Flush() method that some may consider not needed, but these guarantee in general, that all the data written prior the call of the Flush() method is persisted within the stream.

Copy MemoryStream to FileStream and save the file?

I don't understand what I'm doing wrong here. I generate couple of memory streams and in debug-mode I see that they are populated. But when I try to copy MemoryStream to FileStream in order to save the file fileStream is not populated and file is 0bytes long (empty).
Here is my code
if (file.ContentLength > 0)
{
var bytes = ImageUploader.FilestreamToBytes(file); // bytes is populated
using (var inStream = new MemoryStream(bytes)) // inStream is populated
{
using (var outStream = new MemoryStream())
{
using (var imageFactory = new ImageFactory())
{
imageFactory.Load(inStream)
.Resize(new Size(320, 0))
.Format(ImageFormat.Jpeg)
.Quality(70)
.Save(outStream);
}
// outStream is populated here
var fileName = "test.jpg";
using (var fileStream = new FileStream(Server.MapPath("~/content/u/") + fileName, FileMode.CreateNew, FileAccess.ReadWrite))
{
outStream.CopyTo(fileStream); // fileStream is not populated
}
}
}
}
You need to reset the position of the stream before copying.
outStream.Position = 0;
outStream.CopyTo(fileStream);
You used the outStream when saving the file using the imageFactory. That function populated the outStream. While populating the outStream the position is set to the end of the populated area. That is so that when you keep on writing bytes to the steam, it doesn't override existing bytes. But then to read it (for copy purposes) you need to set the position to the start so you can start reading at the start.
If your objective is simply to dump the memory stream to a physical file (e.g. to look at the contents) - it can be done in one move:
System.IO.File.WriteAllBytes(#"C:\\filename", memoryStream.ToArray());
No need to set the stream position first either, since the .ToArray() operation explicitly ignores that, as per #BaconBits comment below https://learn.microsoft.com/en-us/dotnet/api/system.io.memorystream.toarray?view=netframework-4.7.2.
Another alternative to CopyTo is WriteTo.
Advantage:
No need to reset Position.
Usage:
outStream.WriteTo(fileStream);
Function Description:
Writes the entire contents of this memory stream to another stream.

sharpziplib compressed files to be uncompressed externally

I have a scenario where by I want to zip an email attachment using SharpZipLib. Then the end user will open the attachment and will unzip the attached file.
Will the file originally zipped file using SharpZipLib be easily unzipped by other programs for my end user?
It depends on how you use SharpZipLib. There is more than one way to compress the data with this library.
Here is example of method that will create a zip file that you will be able to open in pretty much any zip aware application:
private static byte[] CreateZip(byte[] fileBytes, string fileName)
{
using (var memoryStream = new MemoryStream())
using (var zipStream = new ZipOutputStream(memoryStream))
{
var crc = new Crc32();
crc.Reset();
crc.Update(fileBytes);
var zipEntry =
new ZipEntry(fileName)
{
Crc = crc.Value,
DateTime = DateTime.Now,
Size = fileBytes.Length
};
zipStream.PutNextEntry(zipEntry);
zipStream.Write(fileBytes, 0, fileBytes.Length);
zipStream.Finish();
zipStream.Close();
return memoryStream.ToArray();
}
}
Usage:
var fileBytes = File.ReadAllBytes(#"C:/1.xml");
var zipBytes = CreateZip(fileBytes, "MyFile.xml");
File.WriteAllBytes(#"C:/2.zip", zipBytes);
This CreateZip method is optimized for the cases when you already have bytes in memory and you just want to compress them and send without even saving to disk.

Categories

Resources