Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Files cannot be deleted if network exceptions are thrown #1920

Closed
feimenggo opened this issue Aug 4, 2023 · 25 comments
Closed

Files cannot be deleted if network exceptions are thrown #1920

feimenggo opened this issue Aug 4, 2023 · 25 comments
Labels
i: out of support p: dio Targeting `dio` package platform: io s: bug Something isn't working

Comments

@feimenggo
Copy link

Package

dio

Version

5.3.0

Output of flutter doctor -v

No response

Dart Version

3.10.6

Steps to Reproduce

1.在Windows平台,使用dio上传文件
dio.requestUri(uri, data: file.openRead())

2.当遇到网络错误导致上传失败
Bad state: DioException [unknown]: null
Error: SocketException: Write failed (OS Error: 你的主机中的软件中止了一个已建立的连接。
, errno = 10053), address = private-xxx.cos.ap-guangzhou.myqcloud.com, port = 51980

3.此时想要删除该文件会提示以下错误
PathAccessException: Cannot delete file, path = 'C:\Users\6\AppData\Roaming\xxx\xxx.jpg' (OS Error: 另一个程序正在使用此文件,进程无法访问。
, errno = 32)

4.由于file.openRead() 创建的stream没有done或被cancel,仍然占用着该文件,因此无法删除文件

Expected Result

当遇到网络错误导致上传失败后,需要调用StreamSubscription的cancel方法,关闭流。

Actual Result

由于file.openRead() 创建的stream没有done或被cancel,仍然占用着该文件,因此无法删除文件

@feimenggo feimenggo added h: need triage This issue needs to be categorized s: bug Something isn't working labels Aug 4, 2023
@AlexV525
Copy link
Member

AlexV525 commented Aug 4, 2023

与 dio 貌似没有任何关系,需要你自己维护 stream 的处理?

@AlexV525 AlexV525 added h: need more info Further information is requested and removed h: need triage This issue needs to be categorized s: bug Something isn't working labels Aug 4, 2023
@feimenggo
Copy link
Author

feimenggo commented Aug 4, 2023

与 dio 貌似没有任何关系,需要你自己维护 stream 的处理?

Stream<List> openRead([int? start, int? end]); 方法,返回的是Stream<List>,将其作为参数传给dio了,dio会调用listen()来读数据,StreamSubscription listen(void onData(T event)?, {Function? onError, void onDone()?, bool? cancelOnError});,所以遇到错误的时候需要调用StreamSubscription的cancel方法关闭流。

@AlexV525
Copy link
Member

AlexV525 commented Aug 4, 2023

dio会调用listen()来读数据

我们的代码中没有任何地方对入参 Stream 进行订阅,传入的 Stream 均由 HttpRequest 进行处理。

@feimenggo
Copy link
Author

image 最终是在这里被addStream()的,那当流还没有读完的时候,遇到网络错误了要怎么关闭流呢,是不是应该调用request.abort()方法?

@feimenggo
Copy link
Author

feimenggo commented Aug 4, 2023

image

是不是应该在这里遇到错误的时候,调用 request.close(); 或 request.abort(); 方法呢?

@AlexV525
Copy link
Member

AlexV525 commented Aug 4, 2023

是不是应该在这里遇到错误的时候,调用 request.close(); 或 request.abort(); 方法呢?

可以先行确认是否有改善

@feimenggo
Copy link
Author

是不是应该在这里遇到错误的时候,调用 request.close(); 或 request.abort(); 方法呢?

可以先行确认是否有改善

我通过在上传期间手动断开WiFi网络来模拟SocketException: Write failed (OS Error: 你的主机中的软件中止了一个已建立的连接。,errno = 10053),但是不知道为什么没有catch到SocketException异常。

@AlexV525
Copy link
Member

AlexV525 commented Aug 9, 2023

不需要通过操作物理设备模拟,理论上只需要延迟调用 throw 即可。

@feimenggo
Copy link
Author

在哪个位置调用throw呢?

@AlexV525
Copy link
Member

request 开始之后,delay 一个 throw。

@AlexV525
Copy link
Member

尝试把 io_adapter.dart#L154 改成如图后再试
image

@feimenggo
Copy link
Author

怎么代码不一样,我这里是在150行呢?
image

@AlexV525
Copy link
Member

拉主分支的代码调试呀…

@feimenggo
Copy link
Author

怎么代码不一样,我这里是在150行呢? image

我刚试了dio: 5.3.2,在L150位置加try,没有捕获到异常,而是直接在最开始dio.requestUri()处捕获到异常了

@AlexV525 AlexV525 linked a pull request Sep 21, 2023 that will close this issue
7 tasks
@AlexV525 AlexV525 changed the title 在Windows平台,当遇到网络错误导致上传失败时,无法删除上传的文件 Files cannot be deleted if network exceptions are thrown Oct 24, 2023
@AlexV525 AlexV525 added h: need extra help Extra help is needed p: dio Targeting `dio` package s: bug Something isn't working p: dependency Targeting packages that's used by dio packages platform: io and removed h: need more info Further information is requested labels Oct 24, 2023
@AlexV525
Copy link
Member

问题是否依旧存在?如存在是否可以提供可以直接复现的最小demo?

@feimenggo
Copy link
Author

问题是否依旧存在?如存在是否可以提供可以直接复现的最小demo?

我明天用主分支测试一下

@feimenggo
Copy link
Author

我试了dio: ^5.3.3,问题仍然存在,只在Windows才会遇到。
我是在上传文件的过程中,断开电脑的网络连接,然后删除这个文件,就会提示:文件正在使用,操作无法完成,因为文件已在 xx软件 中打开。

@feimenggo
Copy link
Author

我尝试拉main分支的代码调试
0b2043520120e2705cae29aaf4632a66
报了这个错误,这是什么原因?
2099e96daed7e45272c6e3c2c1d9d325

@AlexV525
Copy link
Member

depedencies:
  dio:
    git:
      url: https://github.com/cfug/dio
      path: dio

@feimenggo
Copy link
Author

遇到依赖冲突了,不知道该如何解决...

Because qcloud_cos_client 0.0.7 depends on dio ^5.3.0 and no versions of qcloud_cos_client match >0.0.7 <0.1.0, qcloud_cos_client ^0.0.7 requires dio from hosted.
So, because app depends on both dio from git and qcloud_cos_client ^0.0.7, version solving failed.

@AlexV525
Copy link
Member

提供可以直接复现的最小demo,此处不再做无关回复。

@AlexV525 AlexV525 added h: need more info Further information is requested and removed h: need extra help Extra help is needed s: bug Something isn't working p: dependency Targeting packages that's used by dio packages labels Nov 17, 2023
@feimenggo
Copy link
Author

我不知道该如何模拟上传接口。
简单来说就是使用dio上传文件到腾讯对象存储,当上传还没结束的时候,断开了电脑的网络连接,此时会提示”你的主机中的软件中止了一个已建立的连接“,此时这个文件文件流仍然被dio持有着没有释放,因此无法移动/删除文件,会提示:文件正在使用,操作无法完成,因为文件已在 xx软件 中打开。

@AlexV525
Copy link
Member

与 dio 无关,等待上游修复。

@AlexV525 AlexV525 closed this as not planned Won't fix, can't repro, duplicate, stale Dec 19, 2023
@AlexV525 AlexV525 added i: out of support and removed h: need extra help Extra help is needed labels Dec 19, 2023
@zpp0196
Copy link

zpp0196 commented Mar 22, 2024

试试 Stream.asBroadcastStream()

参考:localsend/localsend#1228

@NightFeather0615
Copy link

NightFeather0615 commented Mar 22, 2024

试试 Stream.asBroadcastStream()

参考:localsend/localsend#1228

目前看來這應該是最簡潔的方法,缺點就是要等 StreamSubscription 自己取消

這裡有另一種可以馬上釋放 file handle 的解法,更可控但複雜度高不少,其實就是把 .asBroadcastStream() 拆出來用😂

const fileName = "some/file/path";

final file = File(fileName);
final fileStream = file.openRead();
final fileReadController = StreamController<List<int>>();
final fileReadSubscription = fileStream.listen(
  fileReadController.add,
  onError: fileReadController.addError,
  onDone: () => fileReadController.close()
);

final dio = Dio();

try {
  await dio.post(
    "https://httpbun.com/post",
    data: fileReadController.stream
  );
} catch (_) {
  await fileReadSubscription.cancel();
  await file.delete();
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
i: out of support p: dio Targeting `dio` package platform: io s: bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants