Image loss quality after upload via HttpPostedFile.SaveAs(imgPath) - c#

I'm working in a project that doesn't use much of the .net framework and instead work with Request.Form posts.
The code that was being used to save the images (this work, but loss quality) is this one:
files[0].SaveAs(HttpContext.Current.Server.MapPath("../uploads/") + filename);
So I tried to change to this, but now it doesn't even save the image. (the uploadStream.Read(buffer, 0, buffer.Length) comes with the value of 0.
string imagePath = HttpContext.Current.Server.MapPath("../uploads/") + filename;
using (BinaryReader uploadStream = new BinaryReader(files[0].InputStream))
{
using (FileStream fileStream = new FileStream(imagePath, FileMode.Create))
{
byte[] buffer = new byte[32768];
int read;
while ((read = uploadStream.Read(buffer, 0, buffer.Length)) > 0)
{
fileStream.Write(buffer, 0, read);
}
}
}
I built this code basing myself on the question/answers of Image upload without loss of quality. I'm using .net 4.0 on the application.
Is there another way, or what I was doing is the right way and I just missed something? PS: I don't get any error and on the database, I save the correct information, but I don't get the file on the folder. The upload to that folder works, since it work with the SaveAs() method.
PS: I may be wrong, about SaveAs being the reason to loss of quality on the image, but the files[0] come right from the HttpFileCollection and the upload is only there.

Related

C# Getting Runtime error : The process cannot access the file because it is being used by another process

Here is my code:
enter image description here
using System.IO;
namespace Randoms
{
public class Program
{
public static void Main()
{
byte[] buffer = new byte[10240]; // buffer size
string path = #"C:\Users\RAHUL\Desktop\file.txt";
using (FileStream source = new FileStream(path, FileMode.Open, FileAccess.Read))
{
long fileLength = source.Length;
using (FileStream dest = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
{
long totalBytes = 0;
int currentBlockSize = 0;
while ((currentBlockSize = source.Read(buffer, 0, buffer.Length)) > 0)
{
totalBytes += currentBlockSize;
double percentage = (double)totalBytes * 100.0 / fileLength;
dest.Write(buffer, 0, currentBlockSize);
}
}
}
}
}
}
Please check the image which shows the error which I am getting.I have tried to change the FileAccess multiple times but not getting any luck.
This post explains how to both read and write to a file using a single stream:
How to both read and write a file in C#
Consider you may have the file still open from a previous erroneous run. Use a tool like Sysinternals Process Monitor or Unlocker to verify it isn't open by another instance.
How to use Process Monitor:
http://www.manuelmeyer.net/2013/09/tooltips-unlocking-files-with-sysinternals-process-monitor/
Both source and dest are referencing the same file. In the first instance (source) you open it exclusively (ie not shared).
In the second instance (dest) you now want to create the file which you opened in the first instance, but allow it to be shared.
Since the source is already open an in use you cannot write over the top of it using dest.
I think what you may be really want is to have the path parameter for the dest to be different to path parameter for the source, since you are essentially trying to re-write the same data into the same file at the same location right now.

Writing the stream of a png back into another png increases the size. Why?

Can someone explain to me why writing the stream from a PNG image back into another PNG file increases the size of the final output?
Original file: size (28.6 KB), size on disk (32.0 KB)
Output file: size (32.1 KB), size on disk (36.0 KB).
The code for doing this operation is pretty straight forward:
private void button1_Click(object sender, EventArgs e)
{
var result = openFileDialog1.ShowDialog();
if (result == DialogResult.OK)
{
var file = openFileDialog1.FileName;
var stream = new FileStream(file, FileMode.Open);
var newImg = Image.FromStream(stream);
newImg.Save("newPNG.png", ImageFormat.Png);
stream.Close();
}
}
How can I avoid this? I would like the final image to have the exact same size as the original one.
LE: I uploaded the original image if anyone wants to try it out.
cat image
From Wikipedia:
There are five possible filter types that can be specified separately
on each scan line and several possible strategies for searching LZ77
matches. Thus, there are a very large number of different combinations
for how the image can be compressed. Which combination gives the best
compression will depend on the individual image's properties.
That is, there are many ways to compress a PNG, and apparently in your case the original file was compressed in a different way from .NET's default. I'm not sure how much you can affect .NET's output, but there's an override of Image.Save that takes EncoderParameters. You might want to look at that. Link.
I'd bet this due to something like GDI+ saves the image with different settings, and possibly different encodings.
You can retain the same size if you create a new FileStream and read bytes from the first one, then to write them into the second one. Thus copying the file.
stream.Position = 0;
using(FileStream fs = new FileStream("newPNG.png"))
{
int totalBytesRead = 0;
while(totalBytesRead < stream.Length)
{
byte[] byteBuffer = new byte[8192];
int bytesRead = stream.Read(readBytes, 0, byteBuffer.Length);
fs.Write(byteBuffer, totalBytesRead, bytesRead);
totalBytesRead += bytesRead;
}
}
stream.Position = 0;
I set stream.Position to zero both before and after because I don't know where you'll be using this code. Setting it to zero will make the FileStream start reading from the beginning of the file.

FileStream to Bitmap - Parameter is not valid

I have read the posts on this subject but none of them explains it to me clearly enough to be able to fix the problem.
I am trying to upload a file from a local directory to the server.
Here is my code:
string fullPath = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory + #"Images\Readings", PhotoFileName);
Stream s = System.IO.File.OpenRead(fileUpload);
byte[] buffer = new byte[s.Length];
s.Read(buffer, 0, Convert.ToInt32(s.Length));
using (FileStream fs = new FileStream(fullPath, FileMode.Create))
{
fs.Write(buffer, 0, Convert.ToInt32(fs.Length));
Bitmap bmp = new Bitmap((Stream)fs);
bmp.Save(fs, ImageFormat.Jpeg);
}
I keep on getting an Argument Exception: "Parameter is not valid" on line:
Bitmap bmp = new Bitmap((Stream)fs);
Can anyone explain this to me please
There are at least two problems, probably three. First, your copying code is broken:
byte[] buffer = new byte[s.Length];
s.Read(buffer, 0, Convert.ToInt32(s.Length));
You've assumed that this will read all of the data in a single Read call, and ignored the return value for Read. Generally, you'd need to loop round, reading data and writing it (the amount you've just read) to the output stream, until you read the end. However, as of .NET 4, Stream.CopyTo makes this much simpler.
Next is how you're creating the bitmap:
using (FileStream fs = new FileStream(fullPath, FileMode.Create))
{
fs.Write(buffer, 0, Convert.ToInt32(fs.Length));
Bitmap bmp = new Bitmap((Stream)fs);
bmp.Save(fs, ImageFormat.Jpeg);
}
You're trying to read from the stream when you've just written to it - but without "rewinding"... so there's no more data left to read.
Finally, I would strongly advise against using Bitmap.Save to write to the same stream that you're loading the bitmap from. Bitmap will keep a stream open, and read from it when it needs to - if you're trying to write to it at the same time, that could be very confusing.
It's not clear why you're using Bitmap at all, to be honest - if you're just trying to save the file that was uploaded, without any changes, just use:
using (Stream input = File.OpenRead(fileUpload),
output = File.Create(fullPath))
{
input.CopyTo(output);
}
This is assuming that fileUpload really is an appropriate filename - it's not clear why you haven't just written the file to the place you want to write it to straight away, to be honest. Or use File.Copy to copy the file. The above code should work with any stream, so you can change it to save the stream straight from the request...

File upload with c# and streaming

Looking at the different ways to upload a file in .NET, e.g. HttpPostedFile, and using a HttpHandler, I'm trying to understand how the process works in a bit more details.
Specifically how it writes the information to a file.
Say I have the following:
HttpPostedFile file = context.Request.Files[0];
file.SaveAs("c:\temp\file.zip");
The actual file does not get created until the full stream seems to be processed.
Similarly:
using (Stream output = File.OpenWrite("c:\temp\file.zip"))
using (Stream input = file.InputStream)
{
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, bytesRead);
}
}
I would have thought that this would "progressively" write the file as it reads the stream. Looking at the filesystem, it does not seems to do this at all. If I breakpoint inside the while, it does though.
What I'm trying to do, is have it so you upload a file (using a javascript uploader), and poll alongside, whereby the polling ajax request tries to get the fileinfo(file size) of the uploaded file every second. However, it always returns 0 until the upload is complete.
Vimeo seems to be able to do this type of functionality (for IE)?? Is this a .NET limitation, or is there a way to progressively write the file from the stream?
Two points:
First, in Windows, the displayed size of a file is not updated constantly. The file might indeed be growing continually, but the size only increases once.
Second (more likely in this case), the stream might not be flushing to the disk. You could force it to by adding output.Flush() after the call to output.Write(). You might not want to do that, though, since it will probably have a negative impact on performance.
Perhaps you could poll the Length property of the output stream directly, instead of going through the file system.
EDIT:
To make the Length property of the stream accessible to other threads, you could have a field in your class and update it with each read/write:
private long _uploadedByteCount;
void SomeMethod()
{
using (Stream output = File.OpenWrite("c:\temp\file.zip"))
using (Stream input = file.InputStream)
{
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, bytesRead);
Interlocked.Add(ref _uploadedByteCount, bytesRead);
}
}
}
public long GetUploadedByteCount()
{
return _uploadedByteCount;
}

How to save FileSteam as PDF file

I am using a third party tool to get the scanned content from the scanner. On button click it executes the code and gives the content as a FileStream. Now I need to save this FileStream content as a pdf file in to a specified folder.
After saving I need to open the file in browser. How can I save the FileStream as a PDF file?
You can write the stream directly to the output buffer of the response.
So if you're at the point in your code where you have the filestream from the scanner. Simply read bytes from the scanner filestream and write them to the Response.OutputStream
Set the contentType to application/pdf
Make sure you return nothing else. The users browser will do whatever it is configured to do now, either save to disk or show in the browser. You can also save to disk on the server at this point as well in case you wanted a backup.
I'm assuming your file stream is already a pdf, otherwise you'll need to use something like itextsharp to create the pdf.
Edit
Here's some rough and ready code to do it. You'll want to tidy this up, like adding exception trapping to make sure the file stream gets cleaned up properly.
public void SaveToOutput(Stream dataStream)
{
dataStream.Seek(0, SeekOrigin.Begin);
FileStream fileout = File.Create("somepath/file.pdf");
const int chunk = 512;
byte[] buffer = new byte[512];
int bytesread = dataStream.Read(buffer,0,chunk);
while (bytesread == chunk)
{
HttpContext.Current.Response.OutputStream.Write(buffer, 0, chunk);
fileout.Write(buffer, 0, chunk);
bytesread = dataStream.Read(buffer, 0, chunk);
}
HttpContext.Current.Response.OutputStream.Write(buffer, 0, bytesread);
fileout.Write(buffer, 0, bytesread);
fileout.Close();
HttpContext.Current.Response.ContentType = "application/pdf";
}
Simon
You might want to take a look at the C# PDF Library on SourceForge: http://sourceforge.net/projects/pdflibrary/
If I'm understanding you correctly, the third party library is handing you a stream containing the data for the scanned document and you need to write it to a file? If that's the case you need to look up file I/O in C#. Here's a link and an example:
Stream sourceStream = scanner.GetOutput(); // whereever the source stream is
FileStream targetStream = File.OpenWrite(filename, FileMode.Create());
int bytesRead = 0;
byte[] buffer = new byte[2048];
while (true) {
bytesRead = sourceStream.Read(buffer, 0, buffer.length);
if (bytesRead == 0)
break;
targetStream.Write(buffer, 0, bytesRead);
}
sourceStream.Close();
targetStream.Close();
not sure, but maybe check this
http://sourceforge.net/projects/itextsharp/
iTextSharp + FileStream = Corrupt PDF file
Another prominent PDF library (which I have used in the past as well) is iTextSharp. You can take a look at this tutorial on how to convert your Stream to PDF then have the user download it.

Categories