GZipStream works but extension is lost - c#

I am using following code to zip a file and it works fine but when I decompress with WinRar I get the original file name without the extension, any clue why if filename is myReport.xls when I decompress I get only myReport ?
using (var fs = new FileStream(fileName, FileMode.Open))
{
byte[] input = new byte[fs.Length];
fs.Read(input, 0, input.Length);
fs.Close();
using (var fsOutput = new FileStream(zipName, FileMode.Create, FileAccess.Write))
using(var zip = new GZipStream(fsOutput, CompressionMode.Compress))
{
zip.Write(input, 0, input.Length);
zip.Close();
fsOutput.Close();
}
}

GZip compresses only one file - without knowing the name. Therefore if you compress the file myReport.xls you should name it myReport.xls.gz. On decompression the last file extension will be removed so you end up with the original filename.
That its the way how it is used in Unix/Linux for ages...

Very weird indeed. A brief search came up with the following:
http://dotnetzip.codeplex.com/discussions/268293
Which says that GZipStream has no way of knowing the name of the stream that is being written, and suggests you set the FileName property directly.
Hope that helps.

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

C# read IFormFile into byte[]

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

create zip from byte[] and return to browser

I want to create a zip-file and return it to the browser so that it downloads the zip to the downloads-folder.
var images = imageRepository.GetAll(allCountryId);
using (FileStream f2 = new FileStream("SudaAmerica", FileMode.Create))
using (GZipStream gz = new GZipStream(f2, CompressionMode.Compress, false))
{
foreach (var image in images)
{
gz.Write(image.ImageData, 0, image.ImageData.Length);
}
return base.File(gz, "application/zip", "SudaAmerica");
}
i have tried the above but then i get an error saying the stream is disposed.
Is this possible or should i use another library then gzipstream?
The problem here is exactly what it says: you are handing it something based on gz, but gz gets disposed the moment you leave the using.
One option would be to wait until outside the using block, then tell it to use the filename of the thing you just wrote ("SudaAmerica"). However, IMO you shouldn't actually be writing a file here at all. If you use a MemoryStream instead, you can use .ToArray() to get a byte[] of the contents, which you can use in the File method. This requires no IO access, which is a win in about 20 different ways. Well, maybe 3 ways. But...
var images = imageRepository.GetAll(allCountryId);
using (MemoryStream ms = new MemoryStream())
{
using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress, false))
{
foreach (var image in images)
{
gz.Write(image.ImageData, 0, image.ImageData.Length);
}
}
return base.File(ms.ToArray(), "application/zip", "SudaAmerica");
}
Note that a gzip stream is not the same as a .zip archive, so I very much doubt this will have the result you want. Zip archive creation is available elsewhere in the .NET framework, but it is not via GZipStream.
You probably want ZipArchive

Read a PDF into a string or byte[] and write that string/byte[] back to disk

I am having a problem in my app where it reads a PDF from disk, and then has to write it back to a different location later.
The emitted file is not a valid PDF anymore.
In very simplified form, I have tried reading/writing it using
var bytes = File.ReadAllBytes(#"c:\myfile.pdf");
File.WriteAllBytes(#"c:\output.pdf", bytes);
and
var input = new StreamReader(#"c:\myfile.pdf").ReadToEnd();
File.WriteAllText("c:\output.pdf", input);
... and about 100 permutations of the above with various encodings being specified. None of the output files were valid PDFs.
Can someone please lend a hand? Many thanks!!
In C#/.Net 4.0:
using (var i = new FileStream(#"input.pdf", FileMode.Open, FileAccess.Read))
using (var o = File.Create(#"output.pdf"))
i.CopyTo(o);
If you insist on having the byte[] first:
using (var i = new FileStream(#"input.pdf", FileMode.Open, FileAccess.Read))
using (var ms = new MemoryStream())
{
i.CopyTo(ms);
byte[] rawdata = ms.GetBuffer();
using (var o = File.Create(#"output.pdf"))
ms.CopyTo(o);
}
The memory stream may need to be ms.Seek(0, SeekOrigin.Origin) or something like that before the second CopyTo. look it up, or try it out
You're using File.WriteAllText to write your file out.
Try File.WriteAllBytes.

GZipStream only decompresses first line

My GZipStream will only decompress the first line of the file. Extracting the contents via 7-zip works as expected and gives me the entire file contents. It also extracts as expected using gunzip on cygwin and linux, so I expect this is O/S specific (Windows 7).
I'm not certain how to go about troubleshooting this, so any tips on that would help me a great deal. It sounds very similar to this, but using SharpZLib results in the same thing.
Here's what I'm doing:
var inputFile = String.Format(#"{0}\{1}", inputDir, fileName);
var outputFile = String.Format(#"{0}\{1}.gz", inputDir, fileName);
var dcmpFile = String.Format(#"{0}\{1}", outputDir, fileName);
using (var input = File.OpenRead(inputFile))
using (var fileOutput = File.Open(outputFile, FileMode.Append))
using (GZipStream gzOutput = new GZipStream(fileOutput, CompressionMode.Compress, true))
{
input.CopyTo(gzOutput);
}
// Now, decompress
using (FileStream of = new FileStream(outputFile, FileMode.Open, FileAccess.Read))
using (GZipStream ogz = new GZipStream(of, CompressionMode.Decompress, false))
using (FileStream wf = new FileStream(dcmpFile, FileMode.Append, FileAccess.Write))
{
ogz.CopyTo(wf);
}
Your output file only contains a single line (gzipped) - but it contains all of the text data other than the line breaks.
You're repeatedly calling ReadLine() which returns a line of text without the line break and converting that text to bytes. So if you had an input file which had:
abc
def
ghi
You'd end up with an output file which was the compressed version of
abcdefghi
If you don't want that behaviour, why even go through a StreamReader in the first place? Just copy from the input FileStream straight to the GZipStream a block at a time, or use Stream.CopyTo if you're using .NET 4:
// Note how much simpler the code is using File.*
using (var input = File.OpenRead(inputFile))
using (var fileOutput = File.Open(outputFile, FileMode.Append))
using (GZipStream gzOutput = new GZipStream(os, CompressionMode.Compress, true))
{
input.CopyTo(gzOutput);
}
Also note that appending to a compressed file is rarely a good idea, unless you've got some sort of special handling for multiple "chunks" within a single file.

Categories

Resources