Contents

An introduction to the dart:io library

由Mads Ager撰写
2012年3月(2018年9月更新)

dart:io库针对在Flutter和独立Dart VM中运行的代码. 在本文中,我们将通过几个示例使您对dart:io可能实现的功能有一个了解.

Dart是一种单线程编程语言. 如果某个操作阻塞了Dart线程,则该操作完成之前,应用程序不会进行任何处理. 因此,对于可伸缩性,至关重要的是,任何I / O操作都不会阻塞. dart:io 并未阻塞I / O操作,而是使用了受node.js, EventMachineTwisted启发的异步编程模型.

The Dart VM and the event loop

在深入探讨异步I / O操作之前,解释独立Dart VM的运行方式可能会很有用.

在执行服务器端应用程序时,Dart VM会在事件循环中运行,并带有关联的未决异步操作事件队列. 当VM执行完当前代码并结束队列中没有其他未决操作时,VM将终止.

将事件添加到事件队列的一种简单方法是安排将来要调用的函数. 您可以通过创建一个Timer对象来做到这一点. 下面的示例向事件队列注册一个计时器,然后从main()的末尾移出. 由于事件队列中有待处理的操作,因此VM不会在此时终止. 一秒钟后,计时器启动,参数回调中的代码执行. 该代码执行完毕后,事件队列中将不再有任何挂起的操作,并且VM终止.

import 'dart:async';

void main() {
  Timer(Duration(seconds: 1), () => print('timer'));
  print('end of main');
}

在命令行上运行此示例,我们得到:

$ dart timer.dart
end of main
timer

如果使用Timer.periodic构造函数使计时器重复执行 ,则VM不会终止,并且会继续每秒打印出"计时器".

File system access

dart:io库通过FileDirectory类提供对文件和目录的访问.

以下示例显示其自己的源代码. 为了确定正在执行的源代码的位置,我们使用Platform类.

import 'dart:convert';
import 'dart:io';

Future<void> main() async {
  var file = File(Platform.script.toFilePath());
  print("${await (file.readAsString(encoding: ascii))}");
}

注意, readAsString()方法是异步的. 一旦从底层系统中读取了文件,它将返回一个Future ,该Future返回文件的内容. 这种异步性允许Dart线程在等待I / O操作完成的同时执行其他工作.

为了说明更详细的文件操作,我们将示例更改为仅读取第一个分号之前的内容,然后进行打印. 无论是打开文件进行随机访问的文件,或者打开一个:你可以用两种方式做到这一点在数据文件和数据流.

这是一个打开文件进行随机访问操作的版本. 该代码打开文件进行读取,然后一次读取一个字节,直到遇到该字符的char代码为止; .

import 'dart:io';

Future<void> main() async {
  final semicolon = ';'.codeUnitAt(0);
  var result = <int>[];

  final script = File(Platform.script.toFilePath());
  RandomAccessFile file = await script.open(mode: FileMode.read);

  // Deal with each byte.
  while (true) {
    final byte = await file.readByte();
    result.add(byte);
    if (byte == semicolon) {
      print(String.fromCharCodes(result));
      await file.close();
      break;
    }
  }
}

当您看到使用asyncawait ,您会看到"未来在行动". open()readByte()方法均返回Future对象.

当然,这段代码非常简单地使用了随机访问操作. 操作可用于编写,寻找到给定的位置,截断等等.

Let’s implement a version using a stream. The following code opens the file for reading presenting the content as a stream of lists of bytes. Like all streams in Dart you listen on this stream (using await for) and the data is given in chunks.

import 'dart:io';

Future<void> main() async {
  var result = <int>[];

  Stream<List<int>> stream = File(Platform.script.toFilePath()).openRead();
  final semicolon = ';'.codeUnitAt(0);

  await for (var data in stream) {
    for (int i = 0; i < data.length; i++) {
      result.add(data[i]);
      if (data[i] == semicolon) {
        print(String.fromCharCodes(result));
        return;
      }
    }
  }
}

流订阅由await for隐式处理. 退出await for语句(使用breakreturn或未捕获的异常)将取消订阅.

Stream<List<int>>在dart:io中的多个地方使用:使用标准输入,文件,套接字,HTTP连接等时. 同样, IOSink对象用于将数据流传输到stdout,文件,套接字,HTTP连接等.

Interacting with processes

对于简单的情况,使用Process.run()运行一个进程并收集其输出. 当您不需要交互式控制过程时,请使用run() .

import 'dart:io';

Future<void> main() async {
  // List all files in the current directory,
  // in UNIX-like operating systems.
  ProcessResult results = await Process.run('ls', ['-l']);
  print(results.stdout);
}

您还可以通过使用Process.start()创建Process对象来启动流程.

拥有Process对象后,您可以通过以下方式与流程进行交互:将数据写入其stdin接收器,从其stderr和stdout流中读取数据,然后终止该流程. 当流程退出时, exitCode future将以该流程的退出代码完成.

以下示例在单独的进程中运行ls -l ,并将该进程的输出和退出代码输出到stdout. 由于我们对获取行感兴趣,因此我们使用Utf8Decoder (将字节块解码为字符串),然后使用LineSplitter (在行边界处拆分字符串).

import 'dart:convert';
import 'dart:io';

Future<void> main() async {
  final process = await Process.start('ls', ['-l']);
  var lineStream =
      process.stdout.transform(Utf8Decoder()).transform(LineSplitter());
  await for (var line in lineStream) {
    print(line);
  }

  await process.stderr.drain();
  print('exit code: ${await process.exitCode}');
}

请注意, exitCode可以在处理完所有输出行之前完成. 另请注意,我们没有明确关闭该过程. 为了不泄漏资源,我们必须同时侦听stderr和stdout流. 我们使用await for来收听stdout,并使用stderr.drain()来收听(并丢弃)stderr.

除了将输出打印到stdout之外,我们还可以使用流传输类将流程的输出通过管道传输到文件.

import 'dart:io';

Future<void> main() async {
  final output = File('output.txt').openWrite();
  Process process = await Process.start('ls', ['-l']);

  // Wait for the process to finish; get the exit code.
  final exitCode = (await Future.wait([
    process.stdout.pipe(output), // Send stdout to file.
    process.stderr.drain(), // Discard stderr.
    process.exitCode
  ]))[2];

  print('exit code: $exitCode');
}

Writing web servers

dart:io使编写HTTP服务器和客户端变得容易. 要编写一个简单的Web服务器,您所要做的就是创建一个HttpServer并将一个侦听器(使用await for )连接到其HttpRequest流.

这是一个简单的Web服务器,仅对任何请求都回答" Hello,world".

import 'dart:io';

Future<void> main() async {
  final server = await HttpServer.bind('127.0.0.1', 8082);
  await for (HttpRequest request in server) {
    request.response.write('Hello, world');
    await request.response.close();
  }
}

运行此应用程序,然后将浏览器指向" http://127.0.0.1:8082",将为您带来"世界,您好".

让我们添加更多内容并实际提供文件. 我们提供的每个文件的基本路径将是脚本的位置. 如果请求中未指定任何路径,我们将提供index.html. 对于具有路径的请求,我们将尝试查找并提供文件. 如果找不到该文件,我们将以" 404 Not Found"状态答复. 我们利用流接口将从文件中读取的所有数据直接传输到响应流.

import 'dart:io';

Future<void> runServer(String basePath) async {
  final server = await HttpServer.bind('127.0.0.1', 8082);
  await for (HttpRequest request in server) {
    await handleRequest(basePath, request);
  }
}

Future<void> handleRequest(String basePath, HttpRequest request) async {
  final String path = request.uri.toFilePath();
  // PENDING: Do more security checks here.
  final String resultPath = path == '/' ? '/index.html' : path;
  final File file = File('$basePath$resultPath');
  if (await file.exists()) {
    try {
      await request.response.addStream(file.openRead());
    } catch (exception) {
      print('Error happened: $exception');
      await sendInternalError(request.response);
    }
  } else {
    await sendNotFound(request.response);
  }
}

Future<void> sendInternalError(HttpResponse response) async {
  response.statusCode = HttpStatus.internalServerError;
  await response.close();
}

Future<void> sendNotFound(HttpResponse response) async {
  response.statusCode = HttpStatus.notFound;
  await response.close();
}

Future<void> main() async {
  // Compute base path for the request based on the location of the
  // script, and then start the server.
  final script = File(Platform.script.toFilePath());
  await runServer(script.parent.path);
}

编写HTTP客户端与使用HttpClient类非常相似.

Feature requests welcome

dart:io库已经能够执行很多任务. 例如, pub.dev站点使用dart:io.

请让dart:io旋转一下,让我们知道您的想法. 非常欢迎功能要求! 提交错误或功能请求时,请使用dartbug.com. 要查找报告的问题,请搜索library-io标签.

by  ICOPY.SITE