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))}");
}

Notice that the readAsString() method is asynchronous; it returns a Future that returns the contents of the file once the file has been read from the underlying system. This asynchronicity allows the Dart thread to perform other work while waiting for the I/O operation to complete.

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

这是一个打开文件进行随机访问操作的版本. 该代码打开文件进行读取,然后一次读取一个字节,直到遇到该字符的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对象.

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

让我们使用流实现一个版本. 下面的代码打开文件以读取显示的内容,并显示为字节列表流. 与Dart中的所有流一样,您可以在此流上侦听(使用await for ),并且数据以块的形式给出.

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或未捕获的异常)将取消订阅.

在dart:io中的多个地方都使用Stream<List<int>> :在使用stdin,文件,套接字,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未找到"状态. 我们利用流接口将从文件中读取的所有数据直接传输到响应流.

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