bezzad / Downloader

Fast, cross-platform and reliable multipart downloader with asynchronous progress events for .NET applications.
MIT License
1.25k stars 193 forks source link

"The process cannot access the file 'D:\testdownload\100MB-london.bin' because it is being used by another process." #132

Closed AlanWeekend closed 1 year ago

AlanWeekend commented 1 year ago

After closing the software, I use the software package to continue downloading. Normal for the first time, error for the second time, normal for the third time, and error for the fourth time. This cycle. I don't know what's wrong. Please help me

AlanWeekend commented 1 year ago

image This is the code that I execute when I close the software

AlanWeekend commented 1 year ago

image When I open the software again, I will read the package and resume the download

AlanWeekend commented 1 year ago

image

bezzad commented 1 year ago

If you want to stop the download or shutdown application, please use the Stop function instead of Pause. When you use the Pause function, the Download just blocks the current download process and releases it when resuming. But, when you use the Stop function the download process is killed and closed the file streams to use another time. Please check this way and feedback to me know what happened.

bezzad commented 1 year ago

So your code must be like the below codes:

public void SaveDownloadPackages()
{
     if(downloadPackage != null)
     {
           foreach(var download in downloadItems)
           {
                 download.Service.Stop(); 
           }
           ...
     }
}
AlanWeekend commented 1 year ago

image

service has no stop function, i use 3.0.3 version

AlanWeekend commented 1 year ago

image

these are all function in DownloadService

AlanWeekend commented 1 year ago

image

I use DownloadBuilder to create the download object and stop it at the end using the stop function. This is useless, it will still have the same mistakes

bezzad commented 1 year ago

Excuse me, I think you used the DownloadBuilder class. Please call the CancelAsync method which that name is Stop in Fluent models.

bezzad commented 1 year ago

Your meaning is this way still have the same issue with Process cannot access ... ?

AlanWeekend commented 1 year ago

yes

bezzad commented 1 year ago

To explain more and with actual details, I should ask about this issue's steps. Do you accept this way is the same with your scenario which has this issue?

AlanWeekend commented 1 year ago

yes

bezzad commented 1 year ago

I will check this with the tests and come back as soon as possible. Thanks for reporting the issue.

AlanWeekend commented 1 year ago

You're welcome. Thank you for your work

AlanWeekend commented 1 year ago

I created a new winform project to try to reproduce this problem. It works normally. But in my project (winform+webview2), this problem is inevitable. I am trying to create a winform+webview2 demo to reproduce this problem. If it happens, I will tell you at the first time

AlanWeekend commented 1 year ago

image

image

I think I found the reason. CancelAsync closed before it finished executing the program. I added a delay of 1 second, and the problem no longer occurs. Do you have a synchronous cancel function or a callback after execution

bezzad commented 1 year ago

The CancelAsync method is a synchronous state setter that fires a cancellation token and sets a state as the stop for the running tasks. But, the tasks have self organize and decide when close self-connections after seeing this state as Stop. Now, I don't understand why don't close the tasks when your app closed immediately after changing the state to stop!

bezzad commented 1 year ago

Can you share a sample project with me that has this issue?

AlanWeekend commented 1 year ago

I tried to create a new project to reproduce this problem, but it works normally. I will consider how to desensitize the actual project and share it with you

AlanWeekend commented 1 year ago

I sent the sample project to your gmail

bezzad commented 1 year ago

Thanks a lot @AlanWeekend
I will check it and feedback here as soon as possible.

tunger commented 1 year ago

It seems it is because the DownloadPackage.Storage property is per default serialized into JSON (using System.Text.Json), and also deserialized:

{
  "Storage":{"Path":"C:\\dir\\file.txt"}
}
JsonSerializer.Serialize(download.Package);
JsonSerializer.Deserialize<DownloadPackage>(progressFile);

That causes the deserializer to create a Stream which holds a file lock on the partial file. When continuing with await download.DownloadFileTaskAsync(downloadPackage, cancellationToken);, the download fails with mentioned exception as it tries to create another stream.

Workaround: call .Dispose() on the deserialized download package storage stream before continuing download.

downloadPackage.Storage.Dispose();
AlanWeekend commented 1 year ago

It seems it is because the DownloadPackage.Storage property is per default serialized into JSON (using System.Text.Json), and also deserialized:

{
  "Storage":{"Path":"C:\\dir\\file.txt"}
}
JsonSerializer.Serialize(download.Package);
JsonSerializer.Deserialize<DownloadPackage>(progressFile);

That causes the deserializer to create a Stream which holds a file lock on the partial file. When continuing with await download.DownloadFileTaskAsync(downloadPackage, cancellationToken);, the download fails with mentioned exception as it tries to create another stream.

Workaround: call .Dispose() on the deserialized download package storage stream before continuing download.

downloadPackage.Storage.Dispose();

tks, i will try it

bezzad commented 1 year ago

@AlanWeekend please use the new version 3.0.4. In the latest version, you can use the CancelTaskAsync method to wait to complete all tasks.

AlanWeekend commented 1 year ago

@AlanWeekend请使用新版本 3.0.4。在最新版本中,您可以使用该方法等待完成所有任务。CancelTaskAsync

ok