Code fails to download a file more than once - c#

Whenever I click a button in my GUI this code gets executed
this.file_name = #"c:\temp\file_" + DateTime.Now.Ticks / 10000 +".pdf";
client.DownloadFileCompleted +=
new AsyncCompletedEventHandler(pdfDownloadComplete);
client.DownloadFileAsync(new Uri(uri), file_name);
It's supposed to download a pdf file. It works fine the first time I click it, but the second time all it does is it creates an empty file in the temp directory and downloads nothing, I click the same button afterwards nothing new happens.
I cannot figure it out why it won't download more than once.
Later Edit
This is the complete code which is needed http://pastie.org/private/y7na2f4fjdu6anzteoa
I noticed that if I remove the download that checks for content type, the app downloads files without a problem
client.HeadOnly = true;
byte[] body = client.DownloadData(uri); // note should be 0-length
string type = client.ResponseHeaders["content-type"];
client.HeadOnly = false;
Still, I need to know if I'm getting text of a file from that URL so I need to make that call.

Your code has at least one issue that might be root of the problem:
You do an async download inside a using block. I don't really know what happens if the download is still running when the scope of the using block is left, but I guess that it is canceled. You should avoid that problem by using DownloadFile instead of DownloadFileAsync.
Additionally, please check whether it works, when you use the normal WebClient class and not your MyClient class.

Related

Do while loop to check if file exists

I'm writing a C# program for the company I work for that will launch a PHP script which creates a PDF, and then opens the PDF file. Right now, I have:
// launch the PHP page to generate my pdf report
Process.Start(phpFile);
// wait for the report to exist
Thread.Sleep(waitTime);
// open the report
Process.Start(filePath);
Now, I'm not a fan of the whole "Sleep() for a specified time in hopes of the file existing when it's done". So my question is, is it feasible/better to use a do loop and say:
do
{
// Do nothing until the file exists
} while (!File.Exists(filePath));
Why not use a FileSystemWatcher ?
Set the Path and Filter property and subscribe to the Created event.
The problem with using File.Exists is that the file may exist prior to it finishing being written by the first process. I would do something like this instead:
// launch the PHP page to generate my pdf report
Process generator = Process.Start(phpFile);
// wait for the report to exist
generator.WaitForExit();
// open the report
Process.Start(filePath);
Use a FileSystemWatcher and handle the created event. If you want it to happen synchronously use FileSystemWatcher.WaitForChanged with a timeout
I think is a better aproach to look if the file exists in any way. If you rely on a fixed amount of time it can fail or the process will be waiting more than it needs.
What about waiting for the end of the first process ?
Process p = Process.Start(phpFile);
p.WaitForExit();
Process.Start(filePath);

View PDF in Browser in new window on button click and delete file

I am having issue with deleting file created just to send an email with attachment and then view it in browser. now i need to delete this file as this is created to just send email. how can i do this.
here is what i have got so far.
public void SendEmail()
{
EmailClient.Send(mailMessage);
//View PDF Certificate in Browser
ViewPDFinBrowser((string)fileObject);
DeleteGeneratedTempCertificateFile((string)fileObject));
}
public void ViewPDFinBrowser(string filePath)
{
PdfReader reader = new PdfReader(filePath);
MemoryStream ms = new MemoryStream();
PdfStamper stamper = new PdfStamper(reader, ms);
stamper.ViewerPreferences = PdfWriter.PageLayoutSinglePage | PdfWriter.PageModeUseThumbs;
stamper.Close();
Response.Clear();
Response.ContentType = "application/pdf";
Response.OutputStream.Write(ms.GetBuffer(), 0, ms.GetBuffer().Length);
Response.OutputStream.Close();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
public static void DeleteGeneratedTempCertificateFile(Object fileObject)
{
string filePath = (string)fileObject;
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
So here are the steps i need when i call SendEmail()
1) Sends an email with the attachment --> Temp file created
2) view the temp file in the browser
3) delete the temp file
I can understand that as long as file is in response object, i can not do anything with it because i get the error message ("File used by another process). If i close the response stream then file will be deleted but then i cant view it in browser.
i was thinking if i can manage to somehow open the file to view in browser in new window on button click, i will be able to delete the file.
OR
i am thinking i can delete the file after 10 min. as user wont be on site viewing the PDF for more then 1-2 mins.
please advice me one of the solution with example code.
appreciate your time and help.
As others have said, it's better to use the MemoryStream as-is without writing temporary files to the disk. Sometimes implementations of 3rd party components just won't allow this and in such cases after writing the binary contents of the PDF file, be sure to call close (and/or possibly dispose, always check MSDN or 3rd party API docs what the .Close() actually does) to all streams that are no longer needed. In your case close ms and reader after completing the http request.
In most cases, consider implementing the using pattern. See http://msdn.microsoft.com/en-us/library/aa664736.aspx for more details. However remember that there are caveats to this approach too, for example in WCF clients which can cause exceptions within (and thus not actually disposing all contents inside the using clause).
Also, keep in mind any concurrency issues. Keep the temporary file name random enough and consider situations where the file already exists on the local disk (i.e. fail the operation and do not send out binary to the request which the requester is not supposed to see etc).

How to identify whether a download is completed using an ashx handler

In one of our project we need the functionality to download a file from server to client location.
For this we are using an ashx handler to do the operation. Its working perfectly and we are able to download files.
Now we need a requirement like we need to update a field when a download is started and completed. Is there any way to do this.
Once we click the download link the Save as dialog box will appear and after that i think we don't have any control to check the progress. I think we even don't know which button is clicked ie we don't know whether the user is clicked a 'Yes' or 'No'.
Can anyone please suggest a method to know when the download is started and when it has been completed? We are using Asp.Net 2.0 with c#.
The handler used for download is given below
string fileUrl = string.Empty;
if (context.Request["fileUrl"] != null)
{
fileUrl = context.Request["fileUrl"].ToString();
}
string filename = System.IO.Path.GetFileName(fileUrl);
context.Response.ClearContent();
context.Response.ContentType = "application/exe";
context.Response.AddHeader("content-disposition", String.Format("attachment; filename={0}", filename));
context.Response.TransmitFile(fileUrl);
context.Response.Flush();
The file is downloaded from an aspx page method like
private void DownloadExe()
{
string downloadUrl = "Test.exe");
Response.Redirect("Test.ashx?fileUrl=" + downloadUrl, false);
}
Your ASHX handler knwos if download started (since it is actually get called) and when download is completed (end of handler is reached). You may even get some progress server side if you are writing response manually in chunks, this way you also may be able to detect some cases when user cancels download (if writing to response stream fails at some point).
Depending on your needs you may be able to transfer this information to other pages (i.e. via session state) or simply store in some database.
How about this:
Response.BufferOutput = false;
Response.TransmitFile(fileUrl);
//download complete code
If you disable response output buffering then it won't move past the line of code that sends the file to the client until the client has finished receiving it. If they cancel the download half way through it throws a HttpException so the download complete code doesn't get run.
You could also place your download complete code after your call to flush the buffer. But it's better not to enable buffering when sending large binary files to save on server memory.
Ok I had the same problem and jumped over this site:
Check over coockies
This works great for me.

DownloadFileAsync: Multiple Files

I have run into some problems using C# WebClient DownloadFileAsync and hope that you would help me.
Here's my scenario:
I am downloading many files at the same time (http://example.com/abc/1.jpg, http://example.com/abc/efg/2.jpg, etc.) using WebClient DownloadFileAsync.
My current example code is:
while (theres still files in http://example.com/abc/) {
// filename will be something like /abc/1.jpg, /abc/efg/2.jpg
if (!file.exists(directory.getcurrentdirectory()+filename.Replace('/', '\\'))) {
WebClient webClient = new WebClient();
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(downloadProgressChanged);
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(downloadCompleted);
webClient.DownloadFileAsync(new Uri("http://example.com"+filename), Directory.GetCurrentDirectory()+filename.Replace('/', '\\'));
}
How do I make it so that all the files being downloaded is shown in one progress bar?
eg. 1.jpg is 50kb, 2.jpg is 50kb, when 1.jpg completes download the progress bar will show 50%, and 2.jpg will be from 51% to 100% in the progress bar.
Also if the filename is /abc/1.jpg, if my current directory do not have a folder called abc, the download will not work. How do I make it create the folder automatically depending on filename? (eg. /random123/3.jpg, /anotherrandom/4.jpg, etc.)
To give an overall progress counter for you will need a centralised routine that gathers the amount-completed-so-far and total-amount-to-download for each of the in-progress transfers and sums them to calculate the overall totals. Because the transfers are running in parallel you can't just assign a chunk of the progress bar (e.g 51-100%) to each file.
With most APIs you will need to create folders for yourself before writing files into them. Just do this to create all missing folders on the path before you start writing the file:
Directory.CreateDirectory(Path.GetDirectoryName(filename));
To do the progress bar thing, you can use a queue. When something finishes downloading, update the progress bar, and start the next download.
However, I don't think the WebClient class can tell you how much of the download finished, only if it is finished or not.
To verify if the directory exists, use:
if (!System.IO.Directory.Exists(folder_name)) System.IO.Directory.Create(folder_name);
You can get the directory from a path, using the System.IO.Path class.

download a file from server and then delete on server

ok i am downloading a file from a server and i plan to delete the file that i have downloaded on the server after it gets downloaded on the client side..
My download code is working fine but i dont know when to put the command to delete the file.
string filepath = restoredFilename.ToString();
// Create New instance of FileInfo class to get the properties of the file being downloaded
FileInfo myfile = new FileInfo(filepath);
// Checking if file exists
if (myfile.Exists)
{
// Clear the content of the response
Response.ClearContent();
// Add the file name and attachment, which will force the open/cancel/save dialog box to show, to the header
Response.AddHeader("Content-Disposition", "attachment; filename=" + myfile.Name);
//Response.AddHeader("Content-Disposition", "inline; filename=" + myfile.Name);
// Add the file size into the response header
Response.AddHeader("Content-Length", myfile.Length.ToString());
// Set the ContentType
Response.ContentType = ReturnExtension(myfile.Extension.ToLower());
//// Write the file into the response (TransmitFile is for ASP.NET 2.0. In ASP.NET 1.1 you have to use WriteFile instead)
Response.TransmitFile(myfile.FullName);
// End the response
Response.End();
}
Now i know the response.End() will stop every thing and return the value, so is there another way too do so..
I need to call a function
DeleteRestoredFileForGUI(restoredFilename);
to delete the file but dont know where to put it.. i tried putting before and after Response.End() but it does not work..
any help is appreciated... thanks
Add
Response.Flush();
DeleteRestoredFileForGUI(restoredFilename);
after the call to TransmitFile() and ditch the call to Response.End() (you don't need it).
If that does not work, then ditch TransmitFile() and go with:
Stream s = myFile.OpenRead();
int bytesRead = 0;
byte[] buffer = new byte[32 * 1024] //32k buffer
while((bytesRead = s.Read(buffer, 0, buffer.Length)) > 0 &&
Response.IsClientConnected)
{
Response.OutputStream.Write(buffer, 0, bytesRead);
Response.Flush();
}
you can't delete the file straight away as it may not have been downloaded yet. from the server side there is no easy way of telling that the file was successfully downloaded. what if an open/save dialog is opened by the browser? download won't begin until the dialog is acknowledged. (this may not be immediately and/or the dialog may be cancelled)
or, what if it is a large file and the connection is dropped before it is fully downloaded? should it be possible to attempt the download again?
the normally recommended way of dealing with your situation is to do the deletion as a separate process, after a time period which allows you to be (fairly) sure the file is no longer required and/or it can be recreated/restored if need be.
depending on your situation you could have a separate process which periodically removes/processes old files. or, if you have a low volume of traffic, you could check for and delete old files each time a new one is requested.
the identification of old files will likely be based on a file time or associated value in a darabase. either way, if there are potentially lots of files to process you are unlikely to want the overhead of checking very frequently if it is unlikely to identify a lot of files to remove.
also, be sure to way up the consequences of lots of files not being removed ASAP (is disk space really an issue?) against the side effects of possibly deleting them while still needed or creating a performance side effect by checking to zealously.
The general pattern you are following makes me wonder, are you doing this?
Create Data for Client and Save to
Disk Transmit File to Client Delete
File
If you are, you might change your system to work in memory. Since memory is managed in .Net you wouldn't have to do this manual cleanup, and depending on the size of the file this could be a good bit faster too:
Create Data for Client and Save to MemoryStream
Transmit Stream to Client
Since you set the file name in the header, you have two options:
Read the file contents into a string, delete the file, echo/print the string as the body of the message.
Rename the file something like delete-filename.xxx and then have some external process (maybe a cron job?) that goes behind and deletes any files beginning with that prefix.

Categories