I'm using C# to download different files via url and run them afterwards from a physical filepath in the system, however I need to wait for the file until it is completely written and only then run it. The reason is that some files would need more time than others to be saved and I can't really use Thread.Sleep() for this purpose.
I tried this code but it is not that flexible for some reason, as I can't really tell how many tries or how much time it should be until the file is saved. This depends always on the internet connection as well as on the file size.
WebClient client = new WebClient();
var downloadTask = client.DownloadFileTaskAsync(new Uri(url), filepath);
var checkFile = Task.Run(async () => await downloadTask);
WaitForFile(filepath, FileMode.CreateNew);
—
FileStream WaitForFile(string fullPath, FileMode mode)
{
for (int numTries = 0; numTries < 15; numTries++)
{
FileStream fs = null;
try
{
fs = new FileStream(fullPath, mode);
return fs;
}
catch (IOException)
{
if (fs != null)
{
fs.Dispose();
}
Thread.Sleep(50);
}
}
return null;
}
Is there a way to keep waiting until the File.Length > 0?
I would appreciate any help.
Thanks.
You're not awaiting for the file to complete download. Or better said, you're awaiting, in a different thread, and then throwing that result away. Just wait in the very same method and you no longer need a separate way to know when the file is downloaded
WebClient client = new WebClient();
await client.DownloadFileTaskAsync(new Uri(url), filepath);
You can use FileSystemWatcher to get an event when file changes its size or a new file appear https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher.onchanged?view=netcore-3.1
But you can't really tell if the file is fully downloaded this way unless you know its size.
You should just change the code that downloads the file so that it notifies when the file is downloaded.
I think this is all you have to do:
WebClient client = new WebClient();
await client.DownloadFileTaskAsync(new Uri(url), filepath);
// code continues when the file finishes downloading
Related
Note : I use a translation app. Sorry if it's not always very understandable.
I'm developing a UWP application, and I'm having a problem with managing a file type, the CBZ extension.
Some files open without a problem, others the file never opens and blocks the Task.
Here's the code I use :
Task loadEbookTask = Task.Factory.StartNew(() =>
{
Stream streamEbook = WindowsRuntimeStorageExtensions.OpenStreamForReadAsync(ebookFile).Result;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Content = new ZipArchive(streamEbook, ZipArchiveMode.Read, false);
// Pour charque archive, prendre que des extensions valident.
foreach (var file in Content.Entries)
{
string extension = Path.GetExtension(file.Name).ToLower();
bool isFileExtensionOk = EbooksManager.AvailableExtensionsImage.Contains(extension);
if (isFileExtensionOk)
{
ArchivesExploitable.Add(file);
}
}
TotalPage = Convert.ToUInt32(ArchivesExploitable.Count());
});
if (loadEbookTask.Wait(4000))
{
EbookCbz.LoadEbook = EbookLoad.Ok;
}
else
{
EbookCbz.LoadEbook = EbookLoad.Timeout;
}
It's looping on :
Stream streamEbook = WindowsRuntimeStorageExtensions.OpenStreamForReadAsync(ebookFile).Result;
In Visual Studio, memory doesn't go up any more, but the Garbage Collector keeps being called.
With the Task.Wait(4000), it does not stop the Task, so it does not stop turning in background.
And if I open another file, a new task is created, and will turn into a background task.
My question is:
- Is there a method that open a file, and that it is possible to cancel if it exceeds a certain time.
It is this method that is problematic.
Stream streamEbook = WindowsRuntimeStorageExtensions.OpenStreamForReadAsync(ebookFsile).Result;
I change my code to :
byte[] buffer = await ebookFile.ReadBytesAsync();
Stream stream = new MemoryStream(buffer);
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Content = new ZipArchive(stream, ZipArchiveMode.Read, false);
It's fast and if the file is corrupted, there's an exception. It's no longer in memory.
Thanks for your help, I learned a new concept.
I'm trying to build a small program to monitor my pfirewall.log, but I can't seem to open it.
I found quite many (simple) answers, that all kinda say
// use FilesystemWatcher
// open FileStream
// read from last position to end
// output new lines
The problem here is: The file seems to always be opened by another process already. I guess that's the windows process writing to the file, since it's getting written to all the time, as Notepad++ shows me.
Which means, Notepad++ can for some reason do what I can not: Read the file despite it being opened already.
I initialize my monitor in the constructor:
public FirewallLogMonitor(string path)
{
if (!File.Exists(path))
throw new FileNotFoundException("Logfile not found");
this.file = path;
this.lastPosition = 0;
this.monitor = new FileSystemWatcher(Path.GetDirectoryName(path), Path.GetFileName(path));
this.monitor.NotifyFilter = NotifyFilters.Size;
}
And try to read the file on monitor.Changed event:
private void LogFileChanged(object sender, FileSystemEventArgs e)
{
using (FileStream stream = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.Read))
using (StreamReader reader = new StreamReader(stream))
{
stream.Seek(this.lastPosition, SeekOrigin.Begin);
var newLines = reader.ReadToEnd();
this.lastPosition = stream.Length;
var filteredLines = filterLines(newLines);
if (filteredLines.Count > 0)
NewLinesAvailable(this, filteredLines);
}
}
It always throws the IOException on new FileStream(...) to tell me the file is already in use.
Since Notepad++ does it, there has to be a way I can do it too, right?
**Edit: ** A button does this:
public void StartLogging()
{
this.IsRunning = true;
this.monitor.Changed += LogFileChanged;
this.monitor.EnableRaisingEvents = true;
}
**Edit2: ** This is not a duplicate of FileMode and FileAccess and IOException: The process cannot access the file 'filename' because it is being used by another process, since that one assumes I have control over the writing process. Will try the other suggestions, and report back with results.
If i understand your question you can use the notepad++ itself with a plugin to monitor you need to go to:
plugins -> Document Moniter -> Start to monitor
if you dont have this plugin you can download it here:
http://sourceforge.net/projects/npp-plugins/files/DocMonitor/
i'm trying to create an app that downloads a file and then edits this file.
The Problem Im having is once the file is downloaded it doesn't seem to let go of that file, i can download the file to its local storage, i have gotten the file manually from the Iso and its fine. if i use the app to proceed after downloading the file i get the System.UnauthorizedAccessException error, but if i close and open the app and then just edit the file saved in iso it works, like i said its like something is still using the downloaded file.
public async void DownloadTrack(Uri SongUri)
{
var httpClient = new HttpClient();
var data = await httpClient.GetByteArrayAsync(SongUri);
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync("Test.mp3", CreationCollisionOption.ReplaceExisting);
var targetStream = await file.OpenAsync(FileAccessMode.ReadWrite);
await targetStream.AsStreamForWrite().WriteAsync(data, 0, data.Length);
await targetStream.FlushAsync();
}
this code works fine to download the mp3, as ive tested the download file. I have seen if examples where the code ends with
targetStream.Close();
but it doesnt give me that, is there another way to close the download
thanks.
Instead of calling Close() or Dispose() I really like to use using which does the job automatically. So your method could look like this:
public async void DownloadTrack(Uri SongUri)
{
using (HttpClient httpClient = new HttpClient())
{
var data = await httpClient.GetByteArrayAsync(SongUri);
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync("Test.mp3", CreationCollisionOption.ReplaceExisting);
using (var targetStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
await targetStream.AsStreamForWrite().WriteAsync(data, 0, data.Length);
await targetStream.FlushAsync();
}
}
}
I have struggle with downloading few MB excel file from URL and then work with it. Im using VS2010 so i cant use await keyword.
My code follows:
using (WebClient webClient = new WebClient())
{
// setting Windows Authentication
webClient.UseDefaultCredentials = true;
// event fired ExcelToCsv after file is downloaded
webClient.DownloadFileCompleted += (sender, e) => ExcelToCsv(fileName);
// start download
webClient.DownloadFileAsync(new Uri("http://serverx/something/Export.ashx"), exportPath);
}
The line in ExcelToCsv() method
using (FileStream stream = new FileStream(filePath, FileMode.Open))
Throws me an error:
System.IO.IOException: The process cannot access the file because it
is being used by another process.
I tried webClient.DownloadFile() only without an event but it throws same error. Same error is throwed if i do not dispose too. What can i do ?
Temporary workaround may be Sleep() method but its not bullet proof.
Thank you
EDIT:
I tried second approach with standard handling but i have mistake in the code
using (WebClient webClient = new WebClient())
{
// nastaveni ze webClient ma pouzit Windows Authentication
webClient.UseDefaultCredentials = true;
// <--- I HAVE CONVERT ASYNC ERROR IN THIS LINE
webClient.DownloadFileCompleted += new DownloadDataCompletedEventHandler(HandleDownloadDataCompleted);
// spusteni stahovani
webClient.DownloadFile(new Uri("http://czprga2001/Logio_ZelenyKyblik/Export.ashx"), TempDirectory + PSFileName);
}
public delegate void DownloadDataCompletedEventHandler(string fileName);
public event DownloadDataCompletedEventHandler DownloadDataCompleted;
static void HandleDownloadDataCompleted(string fileName)
{
ExcelToCsv(fileName);
}
EDIT: approach 3
I tried this code
while (true)
{
if (isFileLocked(downloadedFile))
{
System.Threading.Thread.Sleep(5000); //wait 5s
ExcelToCsv(fileName);
break;
}
}
and it seems that it is never accessible :/ I dont get it.
Try to use DownloadFile instead of DownloadFileAsync, as you do in Edit 1, like this:
string filename=Path.Combine(TempDirectory, PSFileName);
using (WebClient webClient = new WebClient())
{
// nastaveni ze webClient ma pouzit Windows Authentication
webClient.UseDefaultCredentials = true;
// spusteni stahovani
webClient.DownloadFile(new Uri("http://czprga2001/Logio_ZelenyKyblik/Export.ashx"), filename);
}
ExcelToCsv(filename); //No need to create the event handler if it is not async
From your example it seems that you do not need asynchronous download, so use synchronous download and avoid possible related problems like here.
Also use Path.Combine to combine parts of a path like folder and filename.
There is also a chance that it is locked by something else, use Sysinternals Process Explorer's Find DLL or Handle function to check it.
Use local disk to store downloaded file to prevent problems with network.
I'm trying to download files from my FTP server - multiples at the same time. When i use the DownloadFileAsync .. random files are returned with a byte[] Length of 0. I can 100% confirm the file exists on the server and has content AND there FTP server (running Filezilla Server) isn't erroring and say's the file has been transferred.
private async Task<IList<FtpDataResult>> DownloadFileAsync(FtpFileName ftpFileName)
{
var address = new Uri(string.Format("ftp://{0}{1}", _server, ftpFileName.FullName));
var webClient = new WebClient
{
Credentials = new NetworkCredential(_username, _password)
};
var bytes = await webClient.DownloadDataTaskAsync(address);
using (var stream = new MemoryStream(bytes))
{
// extract the stream data (either files in a zip OR a file);
return result;
}
}
When I try this code, it's slower (of course) but all the files have content.
private async Task<IList<FtpDataResult>> DownloadFileAsync(FtpFileName ftpFileName)
{
var address = new Uri(string.Format("ftp://{0}{1}", _server, ftpFileName.FullName));
var webClient = new WebClient
{
Credentials = new NetworkCredential(_username, _password)
};
// NOTICE: I've removed the AWAIT and a different method.
var bytes = webClient.DownloadData(address);
using (var stream = new MemoryStream(bytes))
{
// extract the stream data (either files in a zip OR a file);
return result;
}
}
Can anyone see what I'm doing wrong, please? Why would the DownloadFileAsync be randomly returning zero bytes?
Try out FtpWebRequest/FtpWebResponse classes. You have more available to you for debugging purposes.
FtpWebRequest - http://msdn.microsoft.com/en-us/library/system.net.ftpwebrequest(v=vs.110).aspx
FtpWebResponse - http://msdn.microsoft.com/en-us/library/system.net.ftpwebresponse(v=vs.110).aspx
Take a look at http://netftp.codeplex.com/. It appears as though almost all methods implement IAsyncResult. There isn't much documentation on how to get started, but I would assume that it is similar to the synchronous FTP classes from the .NET framework. You can install the nuget package here: https://www.nuget.org/packages/System.Net.FtpClient/