// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. part of "dart:io"; /// The modes in which a [File] can be opened. class FileMode { /// The mode for opening a file only for reading. static const read = FileMode._internal(0); /// Mode for opening a file for reading and writing. The file is /// overwritten if it already exists. The file is created if it does not /// already exist. static const write = FileMode._internal(1); /// Mode for opening a file for reading and writing to the /// end of it. The file is created if it does not already exist. static const append = FileMode._internal(2); /// Mode for opening a file for writing *only*. The file is /// overwritten if it already exists. The file is created if it does not /// already exist. static const writeOnly = FileMode._internal(3); /// Mode for opening a file for writing *only* to the /// end of it. The file is created if it does not already exist. static const writeOnlyAppend = FileMode._internal(4); final int _mode; const FileMode._internal(this._mode); } /// Type of lock when requesting a lock on a file. class FileLock { /// Shared file lock. static const shared = FileLock._internal(1); /// Exclusive file lock. static const exclusive = FileLock._internal(2); /// Blocking shared file lock. static const blockingShared = FileLock._internal(3); /// Blocking exclusive file lock. static const blockingExclusive = FileLock._internal(4); final int _type; const FileLock._internal(this._type); } /// A reference to a file on the file system. /// /// A `File` holds a [path] on which operations can be performed. /// You can get the parent directory of the file using [parent], /// a property inherited from [FileSystemEntity]. /// /// Create a new `File` object with a pathname to access the specified file on the /// file system from your program. /// ```dart /// var myFile = File('file.txt'); /// ``` /// The `File` class contains methods for manipulating files and their contents. /// Using methods in this class, you can open and close files, read to and write /// from them, create and delete them, and check for their existence. /// /// When reading or writing a file, you can use streams (with [openRead]), /// random access operations (with [open]), /// or convenience methods such as [readAsString], /// /// Most methods in this class occur in synchronous and asynchronous pairs, /// for example, [readAsString] and [readAsStringSync]. /// Unless you have a specific reason for using the synchronous version /// of a method, prefer the asynchronous version to avoid blocking your program. /// /// ## If path is a link /// /// If [path] is a symbolic link, rather than a file, /// then the methods of `File` operate on the ultimate target of the /// link, except for [delete] and [deleteSync], which operate on /// the link. /// /// ## Read from a file /// /// The following code sample reads the entire contents from a file as a string /// using the asynchronous [readAsString] method: /// ```dart /// import 'dart:async'; /// import 'dart:io'; /// /// void main() { /// File('file.txt').readAsString().then((String contents) { /// print(contents); /// }); /// } /// ``` /// A more flexible and useful way to read a file is with a [Stream]. /// Open the file with [openRead], which returns a stream that /// provides the data in the file as chunks of bytes. /// Read the stream to process the file contents when available. /// You can use various transformers in succession to manipulate the /// file content into the required format, or to prepare it for output. /// /// You might want to use a stream to read large files, /// to manipulate the data with transformers, /// or for compatibility with another API, such as [WebSocket]s. /// ```dart /// import 'dart:io'; /// import 'dart:convert'; /// import 'dart:async'; /// /// void main() async { /// final file = File('file.txt'); /// Stream lines = file.openRead() /// .transform(utf8.decoder) // Decode bytes to UTF-8. /// .transform(LineSplitter()); // Convert stream to individual lines. /// try { /// await for (var line in lines) { /// print('$line: ${line.length} characters'); /// } /// print('File is now closed.'); /// } catch (e) { /// print('Error: $e'); /// } /// } /// ``` /// ## Write to a file /// /// To write a string to a file, use the [writeAsString] method: /// ```dart /// import 'dart:io'; /// /// void main() async { /// final filename = 'file.txt'; /// var file = await File(filename).writeAsString('some content'); /// // Do something with the file. /// } /// ``` /// You can also write to a file using a [Stream]. Open the file with /// [openWrite], which returns an [IOSink] to which you can write data. /// Be sure to close the sink with the [IOSink.close] method. /// ```dart /// import 'dart:io'; /// /// void main() async { /// var file = File('file.txt'); /// var sink = file.openWrite(); /// sink.write('FILE ACCESSED ${DateTime.now()}\n'); /// await sink.flush(); /// /// // Close the IOSink to free system resources. /// await sink.close(); /// } /// ``` /// ## The use of asynchronous methods /// /// To avoid unintentional blocking of the program, /// several methods are asynchronous and return a [Future]. For example, /// the [length] method, which gets the length of a file, returns a [Future]. /// Wait for the future to get the result when it's ready. /// ```dart /// import 'dart:io'; /// /// void main() async { /// final file = File('file.txt'); /// /// var length = await file.length(); /// print(length); /// } /// ``` /// In addition to length, the [exists], [lastModified], [stat], and /// other methods, are asynchronous. /// /// ## Special 'nul' file /// /// On Linux and Mac '/dev/null' and on Windows '\\?\NUL' refer to a special file, /// such that all writes to it get consumed and disappear, and all reads produce empty /// output. Note that on Windows 'nul'(without '\\?\'-prefix) refers to a regular file /// named 'nul' in current directory. /// /// ## Other resources /// /// * The [Files and directories](https://dart.dev/guides/libraries/library-tour#files-and-directories) /// section of the library tour. /// /// * [Write Command-Line Apps](https://dart.dev/tutorials/server/cmdline), /// a tutorial about writing command-line apps, includes information about /// files and directories. @pragma("vm:entry-point") abstract interface class File implements FileSystemEntity { /// Creates a [File] object. /// /// If [path] is a relative path, it will be interpreted relative to the /// current working directory (see [Directory.current]), when used. /// /// If [path] is an absolute path, it will be immune to changes to the /// current working directory. @pragma("vm:entry-point") factory File(String path) { final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _File(path); } return overrides.createFile(path); } /// Create a [File] object from a URI. /// /// If [uri] cannot reference a file this throws [UnsupportedError]. factory File.fromUri(Uri uri) => File(uri.toFilePath()); /// Creates a [File] object from a raw path. /// /// A raw path is a sequence of bytes, as paths are represented by the OS. @pragma("vm:entry-point") factory File.fromRawPath(Uint8List rawPath) { // TODO(bkonyi): Handle overrides. return _File.fromRawPath(rawPath); } /// Creates the file. /// /// Returns a `Future` that completes with /// the file when it has been created. /// /// If [recursive] is `false`, the default, the file is created only if /// all directories in its path already exist. If [recursive] is `true`, any /// non-existing parent paths are created first. /// /// If [exclusive] is `true` and to-be-created file already exists, this /// operation completes the future with a [PathExistsException]. /// /// If [exclusive] is `false`, existing files are left untouched by [create]. /// Calling [create] on an existing file still might fail if there are /// restrictive permissions on the file. /// /// Completes the future with a [FileSystemException] if the operation fails. Future create({bool recursive = false, bool exclusive = false}); /// Synchronously creates the file. /// /// If [recursive] is `false`, the default, the file is created /// only if all directories in its path already exist. /// If [recursive] is `true`, all non-existing parent paths are created first. /// /// If [exclusive] is `true` and to-be-created file already exists, then /// a [FileSystemException] is thrown. /// /// If [exclusive] is `false`, existing files are left untouched by /// [createSync]. Calling [createSync] on an existing file still might fail /// if there are restrictive permissions on the file. /// /// Throws a [FileSystemException] if the operation fails. void createSync({bool recursive = false, bool exclusive = false}); /// Renames this file. /// /// Returns a `Future` that completes /// with a [File] for the renamed file. /// /// If [newPath] is a relative path, it is resolved against /// the current working directory ([Directory.current]). /// This means that simply changing the name of a file, /// but keeping it the original directory, /// requires creating a new complete path with the new name /// at the end. Example: /// ```dart /// Future changeFileNameOnly(File file, String newFileName) { /// var path = file.path; /// var lastSeparator = path.lastIndexOf(Platform.pathSeparator); /// var newPath = path.substring(0, lastSeparator + 1) + newFileName; /// return file.rename(newPath); /// } /// ``` /// On some platforms, a rename operation cannot move a file between /// different file systems. If that is the case, instead [copy] the /// file to the new location and then remove the original. /// /// If [newPath] identifies an existing file or link, that entity is /// removed first. If [newPath] identifies an existing directory, the /// operation fails and the future completes with a [FileSystemException]. Future rename(String newPath); /// Synchronously renames this file. /// /// Returns a [File] for the renamed file. /// /// If [newPath] is a relative path, it is resolved against /// the current working directory ([Directory.current]). /// This means that simply changing the name of a file, /// but keeping it the original directory, /// requires creating a new complete path with the new name /// at the end. Example: /// ```dart /// File changeFileNameOnlySync(File file, String newFileName) { /// var path = file.path; /// var lastSeparator = path.lastIndexOf(Platform.pathSeparator); /// var newPath = path.substring(0, lastSeparator + 1) + newFileName; /// return file.renameSync(newPath); /// } /// ``` /// On some platforms, a rename operation cannot move a file between /// different file systems. If that is the case, instead [copySync] the /// file to the new location and then [deleteSync] the original. /// /// If [newPath] identifies an existing file or link, that entity is /// removed first. If [newPath] identifies an existing directory the /// operation throws a [FileSystemException]. File renameSync(String newPath); /// Deletes this [File]. /// /// If [recursive] is `false`: /// /// * If [path] corresponds to a regular file, named pipe or socket, then /// that path is deleted. If [path] corresponds to a link, and that link /// resolves to a file, then the link at [path] will be deleted. In all /// other cases, [delete] completes with a [FileSystemException]. /// /// If [recursive] is `true`: /// /// * The [FileSystemEntity] at [path] is deleted regardless of type. If /// [path] corresponds to a file or link, then that file or link is /// deleted. If [path] corresponds to a directory, then it and all /// sub-directories and files in those directories are deleted. Links /// are not followed when deleting recursively. Only the link is deleted, /// not its target. This behavior allows [delete] to be used to /// unconditionally delete any file system object. /// /// If this [File] cannot be deleted, then [delete] completes with a /// [FileSystemException]. Future delete({bool recursive = false}); /// Synchronously deletes this [File]. /// /// If [recursive] is `false`: /// /// * If [path] corresponds to a regular file, named pipe or socket, then /// that path is deleted. If [path] corresponds to a link, and that link /// resolves to a file, then the link at [path] will be deleted. In all /// other cases, [delete] throws a [FileSystemException]. /// /// If [recursive] is `true`: /// /// * The [FileSystemEntity] at [path] is deleted regardless of type. If /// [path] corresponds to a file or link, then that file or link is /// deleted. If [path] corresponds to a directory, then it and all /// sub-directories and files in those directories are deleted. Links /// are not followed when deleting recursively. Only the link is deleted, /// not its target. This behavior allows [delete] to be used to /// unconditionally delete any file system object. /// /// If this [File] cannot be deleted, then [delete] throws a /// [FileSystemException]. void deleteSync({bool recursive = false}); /// Copies this file. /// /// If [newPath] is a relative path, it is resolved against /// the current working directory ([Directory.current]). /// /// Returns a `Future` that completes /// with a [File] for the copied file. /// /// If [newPath] identifies an existing file, that file is /// removed first. If [newPath] identifies an existing directory, the /// operation fails and the future completes with an exception. Future copy(String newPath); /// Synchronously copies this file. /// /// If [newPath] is a relative path, it is resolved against /// the current working directory ([Directory.current]). /// /// Returns a [File] for the copied file. /// /// If [newPath] identifies an existing file, that file is /// removed first. If [newPath] identifies an existing directory the /// operation fails and an exception is thrown. File copySync(String newPath); /// The length of the file. /// /// Returns a `Future` that completes with the length in bytes. Future length(); /// The length of the file provided synchronously. /// /// Throws a [FileSystemException] if the operation fails. int lengthSync(); /// A [File] with the absolute path of [path]. /// /// The absolute path is computed by prefixing /// a relative path with the current working directory, /// or returning an absolute path unchanged. File get absolute; /// The last-accessed time of the file. /// /// Returns a `Future` that completes with the date and time when the /// file was last accessed, if the information is available. /// /// Throws a [FileSystemException] if the operation fails. Future lastAccessed(); /// The last-accessed time of the file. /// /// Returns the date and time when the file was last accessed, /// if the information is available. Blocks until the information can be returned /// or it is determined that the information is not available. /// /// Throws a [FileSystemException] if the operation fails. DateTime lastAccessedSync(); /// Modifies the time the file was last accessed. /// /// Returns a [Future] that completes once the operation has completed. /// /// Throws a [FileSystemException] if the time cannot be set. Future setLastAccessed(DateTime time); /// Synchronously modifies the time the file was last accessed. /// /// Throws a [FileSystemException] if the time cannot be set. void setLastAccessedSync(DateTime time); /// Get the last-modified time of the file. /// /// Returns a `Future` that completes with the date and time when the /// file was last modified, if the information is available. /// /// Throws a [FileSystemException] if the operation fails. Future lastModified(); /// Get the last-modified time of the file. /// /// Returns the date and time when the file was last modified, /// if the information is available. Blocks until the information can be returned /// or it is determined that the information is not available. /// /// Throws a [FileSystemException] if the operation fails. DateTime lastModifiedSync(); /// Modifies the time the file was last modified. /// /// Returns a [Future] that completes once the operation has completed. /// /// Throws a [FileSystemException] if the time cannot be set. Future setLastModified(DateTime time); /// Synchronously modifies the time the file was last modified. /// /// If the attributes cannot be set, throws a [FileSystemException]. void setLastModifiedSync(DateTime time); /// Opens the file for random access operations. /// /// Returns a `Future` that completes with the opened /// random access file. [RandomAccessFile]s must be closed using the /// [RandomAccessFile.close] method. /// /// Files can be opened in three modes: /// /// * [FileMode.read]: open the file for reading. /// /// * [FileMode.write]: open the file for both reading and writing and /// truncate the file to length zero. If the file does not exist the /// file is created. /// /// * [FileMode.append]: same as [FileMode.write] except that the file is /// not truncated. /// /// Throws a [FileSystemException] if the operation fails. Future open({FileMode mode = FileMode.read}); /// Synchronously opens the file for random access operations. /// /// The result is a [RandomAccessFile] on which random access operations /// can be performed. Opened [RandomAccessFile]s must be closed using /// the [RandomAccessFile.close] method. /// /// See [open] for information on the [mode] argument. /// /// Throws a [FileSystemException] if the operation fails. RandomAccessFile openSync({FileMode mode = FileMode.read}); /// Creates a new independent [Stream] for the contents of this file. /// /// If [start] is present, the file will be read from byte-offset [start]. /// Otherwise from the beginning (index 0). /// /// If [end] is present, only bytes up to byte-index [end] will be read. /// Otherwise, until end of file. /// /// In order to make sure that system resources are freed, the stream /// must be read to completion or the subscription on the stream must /// be cancelled. /// /// If [File] is a [named pipe](https://en.wikipedia.org/wiki/Named_pipe) /// then the returned [Stream] will wait until the write side of the pipe /// is closed before signaling "done". If there are no writers attached /// to the pipe when it is opened, then [Stream.listen] will wait until /// a writer opens the pipe. /// /// An error opening or reading the file will appear as a /// [FileSystemException] error event on the returned [Stream], after which /// the [Stream] is closed. For example: /// /// ```dart /// // This example will print the "Error reading file" message and the /// // `await for` loop will complete normally, without seeing any data /// // events. /// final stream = File('does-not-exist') /// .openRead() /// .handleError((e) => print('Error reading file: $e')); /// await for (final data in stream) { /// print(data); /// } /// ``` Stream> openRead([int? start, int? end]); /// Creates a new independent [IOSink] for the file. /// /// The [IOSink] must be closed when no longer used, to free /// system resources. /// /// An [IOSink] for a file can be opened in two modes: /// /// * [FileMode.write]: truncates the file to length zero. /// * [FileMode.append]: sets the initial write position to the end /// of the file. /// /// When writing strings through the returned [IOSink] the encoding /// specified using [encoding] will be used. The returned [IOSink] /// has an `encoding` property which can be changed after the /// [IOSink] has been created. /// /// The returned [IOSink] does not transform newline characters (`"\n"`) to /// the platform's conventional line ending (e.g. `"\r\n"` on Windows). Write /// a [Platform.lineTerminator] if a platform-specific line ending is needed. /// /// If an error occurs while opening or writing to the file, the [IOSink.done] /// [IOSink.flush], and [IOSink.close] futures will all complete with a /// [FileSystemException]. You must handle errors from the [IOSink.done] /// future or the error will be uncaught. /// /// For example, [FutureExtensions.ignore] the [IOSink.done] error and /// remember to `await` the [IOSink.flush] and [IOSink.close] calls within a /// `try`/`catch`: /// /// ```dart /// final sink = File('/tmp').openWrite(); // Can't write to /tmp /// sink.done.ignore(); /// sink.write("This is a test"); /// try { /// // If one of these isn't awaited, then errors will pass silently! /// await sink.flush(); /// await sink.close(); /// } on FileSystemException catch (e) { /// print('Error writing file: $e'); /// } /// ``` /// /// To handle errors asynchronously outside of the context of [IOSink.flush] /// and [IOSink.close], you can [Future.catchError] the [IOSink.done]. /// /// ```dart /// final sink = File('/tmp').openWrite(); // Can't write to /tmp /// sink.done.catchError((e) { /// // Handle the error. /// }); /// ``` IOSink openWrite({FileMode mode = FileMode.write, Encoding encoding = utf8}); /// Reads the entire file contents as a list of bytes. /// /// Returns a `Future` that completes with the list of bytes that /// is the contents of the file. Future readAsBytes(); /// Synchronously reads the entire file contents as a list of bytes. /// /// Throws a [FileSystemException] if the operation fails. Uint8List readAsBytesSync(); /// Reads the entire file contents as a string using the given /// [Encoding]. /// /// Returns a `Future` that completes with the string once /// the file contents has been read. Future readAsString({Encoding encoding = utf8}); /// Synchronously reads the entire file contents as a string using the /// given [Encoding]. /// /// Throws a [FileSystemException] if the operation fails. String readAsStringSync({Encoding encoding = utf8}); /// Reads the entire file contents as lines of text using the given /// [Encoding]. /// /// Returns a `Future>` that completes with the lines /// once the file contents has been read. Future> readAsLines({Encoding encoding = utf8}); /// Synchronously reads the entire file contents as lines of text /// using the given [Encoding]. /// /// Throws a [FileSystemException] if the operation fails. List readAsLinesSync({Encoding encoding = utf8}); /// Writes a list of bytes to a file. /// /// Opens the file, writes the list of bytes to it, and closes the file. /// Returns a `Future` that completes with this [File] object once /// the entire operation has completed. /// /// By default [writeAsBytes] creates the file for writing and truncates the /// file if it already exists. In order to append the bytes to an existing /// file, pass [FileMode.append] as the optional mode parameter. /// /// The elements of [bytes] should be integers in the range 0 to 255. /// Any integer, which is not in that range, is converted to a byte before /// being written. The conversion is equivalent to doing /// `value.toUnsigned(8)`. /// /// If the argument [flush] is set to `true`, the data written will be /// flushed to the file system before the returned future completes. Future writeAsBytes( List bytes, { FileMode mode = FileMode.write, bool flush = false, }); /// Synchronously writes a list of bytes to a file. /// /// Opens the file, writes the list of bytes to it and closes the file. /// /// By default [writeAsBytesSync] creates the file for writing and truncates /// the file if it already exists. In order to append the bytes to an existing /// file, pass [FileMode.append] as the optional mode parameter. /// /// The elements of [bytes] should be integers in the range 0 to 255. /// Any integer, which is not in that range, is converted to a byte before /// being written. The conversion is equivalent to doing /// `value.toUnsigned(8)`. /// /// If the [flush] argument is set to `true` data written will be /// flushed to the file system before returning. /// /// Throws a [FileSystemException] if the operation fails. void writeAsBytesSync( List bytes, { FileMode mode = FileMode.write, bool flush = false, }); /// Writes a string to a file. /// /// Opens the file, writes the string in the given encoding, and closes the /// file. Returns a `Future` that completes with this [File] object /// once the entire operation has completed. /// /// By default [writeAsString] creates the file for writing and truncates the /// file if it already exists. In order to append the bytes to an existing /// file, pass [FileMode.append] as the optional mode parameter. /// /// If the argument [flush] is set to `true`, the data written will be /// flushed to the file system before the returned future completes. /// /// This method does not transform newline characters (`"\n"`) to the /// platform conventional line ending (e.g. `"\r\n"` on Windows). Use /// [Platform.lineTerminator] to separate lines in [contents] if platform /// conventional line endings are needed. Future writeAsString( String contents, { FileMode mode = FileMode.write, Encoding encoding = utf8, bool flush = false, }); /// Synchronously writes a string to a file. /// /// Opens the file, writes the string in the given encoding, and closes the /// file. /// /// By default [writeAsStringSync] creates the file for writing and /// truncates the file if it already exists. In order to append the bytes /// to an existing file, pass [FileMode.append] as the optional mode /// parameter. /// /// If the [flush] argument is set to `true`, data written will be /// flushed to the file system before returning. /// /// This method does not transform newline characters (`"\n"`) to the /// platform conventional line ending (e.g. `"\r\n"` on Windows). Use /// [Platform.lineTerminator] to separate lines in [contents] if platform /// conventional line endings are needed. /// /// Throws a [FileSystemException] if the operation fails. void writeAsStringSync( String contents, { FileMode mode = FileMode.write, Encoding encoding = utf8, bool flush = false, }); /// Get the path of the file. String get path; } /// Random access to the data in a file. /// /// `RandomAccessFile` objects are obtained by calling the /// `open` method on a [File] object. /// /// A `RandomAccessFile` has both asynchronous and synchronous /// methods. The asynchronous methods all return a [Future] /// whereas the synchronous methods will return the result directly, /// and block the current isolate until the result is ready. /// /// At most one asynchronous method can be pending on a given `RandomAccessFile` /// instance at the time. If another asynchronous method is called when one is /// already in progress, a [FileSystemException] is thrown. /// /// If an asynchronous method is pending, it is also not possible to call any /// synchronous methods. This will also throw a [FileSystemException]. abstract interface class RandomAccessFile { /// Closes the file. /// /// Returns a [Future] that completes when it has been closed. Future close(); /// Synchronously closes the file. /// /// Throws a [FileSystemException] if the operation fails. void closeSync(); /// Reads a byte from the file. /// /// Returns a `Future` that completes with the byte, /// or with -1 if end-of-file has been reached. Future readByte(); /// Synchronously reads a single byte from the file. /// /// If end-of-file has been reached -1 is returned. /// /// Throws a [FileSystemException] if the operation fails. int readByteSync(); /// Reads up to [count] bytes from a file. /// /// May return fewer than [count] bytes. This can happen, for example, when /// reading past the end of a file or when reading from a pipe that does not /// currently contain additional data. /// /// An empty [Uint8List] will only be returned when reading past the end of /// the file or when [count] is `0`. Future read(int count); /// Synchronously reads up to [count] bytes from a file /// /// May return fewer than [count] bytes. This can happen, for example, when /// reading past the end of a file or when reading from a pipe that does not /// currently contain additional data. /// /// An empty [Uint8List] will only be returned when reading past the end of /// the file or when [count] is `0`. /// /// Throws a [FileSystemException] if the operation fails. Uint8List readSync(int count); /// Reads bytes into an existing [buffer]. /// /// Reads bytes and writes them into the range of [buffer] /// from [start] to [end]. /// The [start] must be non-negative and no greater than [buffer].length. /// If [end] is omitted, it defaults to [buffer].length. /// Otherwise [end] must be no less than [start] /// and no greater than [buffer].length. /// /// Returns the number of bytes read. This maybe be less than `end - start` /// if the file doesn't have that many bytes to read. Future readInto(List buffer, [int start = 0, int? end]); /// Synchronously reads into an existing [buffer]. /// /// Reads bytes and writes them into the range of [buffer] /// from [start] to [end]. /// The [start] must be non-negative and no greater than [buffer].length. /// If [end] is omitted, it defaults to [buffer].length. /// Otherwise [end] must be no less than [start] /// and no greater than [buffer].length. /// /// Returns the number of bytes read. This maybe be less than `end - start` /// if the file doesn't have that many bytes to read. /// /// Throws a [FileSystemException] if the operation fails. int readIntoSync(List buffer, [int start = 0, int? end]); /// Writes a single byte to the file. /// /// Returns a `Future` that completes with this /// random access file when the write completes. Future writeByte(int value); /// Synchronously writes a single byte to the file. /// /// Returns 1 on success. /// /// Throws a [FileSystemException] if the operation fails. int writeByteSync(int value); /// Writes from a [buffer] to the file. /// /// Will read the buffer from index [start] to index [end]. /// The [start] must be non-negative and no greater than [buffer].length. /// If [end] is omitted, it defaults to [buffer].length. /// Otherwise [end] must be no less than [start] /// and no greater than [buffer].length. /// /// Returns a `Future` that completes with this /// [RandomAccessFile] when the write completes. Future writeFrom( List buffer, [ int start = 0, int? end, ]); /// Synchronously writes from a [buffer] to the file. /// /// Will read the buffer from index [start] to index [end]. /// The [start] must be non-negative and no greater than [buffer].length. /// If [end] is omitted, it defaults to [buffer].length. /// Otherwise [end] must be no less than [start] /// and no greater than [buffer].length. /// /// Throws a [FileSystemException] if the operation fails. void writeFromSync(List buffer, [int start = 0, int? end]); /// Writes a string to the file using the given [Encoding]. /// /// Returns a `Future` that completes with this /// random access file when the write completes. Future writeString( String string, { Encoding encoding = utf8, }); /// Synchronously writes a single string to the file using the given /// [Encoding]. /// /// Throws a [FileSystemException] if the operation fails. void writeStringSync(String string, {Encoding encoding = utf8}); /// Gets the current byte position in the file. /// /// Returns a `Future` that completes with the position. Future position(); /// Synchronously gets the current byte position in the file. /// /// Throws a [FileSystemException] if the operation fails. int positionSync(); /// Sets the byte position in the file. /// /// Returns a `Future` that completes with this /// random access file when the position has been set. Future setPosition(int position); /// Synchronously sets the byte position in the file. /// /// Throws a [FileSystemException] if the operation fails. void setPositionSync(int position); /// Truncates (or extends) the file to [length] bytes. /// /// Returns a `Future` that completes with this /// random access file when the truncation has been performed. Future truncate(int length); /// Synchronously truncates (or extends) the file to [length] bytes. /// /// Throws a [FileSystemException] if the operation fails. void truncateSync(int length); /// Gets the length of the file. /// /// Returns a `Future` that completes with the length in bytes. Future length(); /// Synchronously gets the length of the file. /// /// Throws a [FileSystemException] if the operation fails. int lengthSync(); /// Flushes the contents of the file to disk. /// /// Returns a `Future` that completes with this /// random access file when the flush operation completes. Future flush(); /// Synchronously flushes the contents of the file to disk. /// /// Throws a [FileSystemException] if the operation fails. void flushSync(); /// Locks the file or part of the file. /// /// By default an exclusive lock will be obtained, but that can be overridden /// by the [mode] argument. /// /// Locks the byte range from [start] to [end] of the file, with the /// byte at position `end` not included. If no arguments are /// specified, the full file is locked, If only `start` is specified /// the file is locked from byte position `start` to the end of the /// file, no matter how large it grows. It is possible to specify an /// explicit value of `end` which is past the current length of the file. /// /// To obtain an exclusive lock on a file, it must be opened for writing. /// /// If [mode] is [FileLock.exclusive] or [FileLock.shared], an error is /// signaled if the lock cannot be obtained. If [mode] is /// [FileLock.blockingExclusive] or [FileLock.blockingShared], the /// returned [Future] is resolved only when the lock has been obtained. /// /// *NOTE* file locking does have slight differences in behavior across /// platforms: /// /// On Linux and OS X this uses advisory locks, which have the /// surprising semantics that all locks associated with a given file /// are removed when *any* file descriptor for that file is closed by /// the process. Note that this does not actually lock the file for /// access. Also note that advisory locks are on a process /// level. This means that several isolates in the same process can /// obtain an exclusive lock on the same file. /// /// On Windows the regions used for lock and unlock needs to match. If that /// is not the case unlocking will result in the OS error "The segment is /// already unlocked". /// /// On Windows, locking a file associates the lock with the specific file /// handle used to acquire the lock. If the same file is opened again with a /// different handle (even within the same process), attempts to write to the /// locked region using the new handle will fail. To ensure successful writes /// after locking, use the same [RandomAccessFile] object that acquired the /// lock for subsequent write operations. Future lock([ FileLock mode = FileLock.exclusive, int start = 0, int end = -1, ]); /// Synchronously locks the file or part of the file. /// /// By default an exclusive lock will be obtained, but that can be overridden /// by the [mode] argument. /// /// Locks the byte range from [start] to [end] of the file ,with the /// byte at position `end` not included. If no arguments are /// specified, the full file is locked, If only `start` is specified /// the file is locked from byte position `start` to the end of the /// file, no matter how large it grows. It is possible to specify an /// explicit value of `end` which is past the current length of the file. /// /// To obtain an exclusive lock on a file it must be opened for writing. /// /// If [mode] is [FileLock.exclusive] or [FileLock.shared], an exception is /// thrown if the lock cannot be obtained. If [mode] is /// [FileLock.blockingExclusive] or [FileLock.blockingShared], the /// call returns only after the lock has been obtained. /// /// *NOTE* file locking does have slight differences in behavior across /// platforms: /// /// On Linux and OS X this uses advisory locks, which have the /// surprising semantics that all locks associated with a given file /// are removed when *any* file descriptor for that file is closed by /// the process. Note that this does not actually lock the file for /// access. Also note that advisory locks are on a process /// level. This means that several isolates in the same process can /// obtain an exclusive lock on the same file. /// /// On Windows the regions used for lock and unlock needs to match. If that /// is not the case unlocking will result in the OS error "The segment is /// already unlocked". /// /// On Windows, locking a file associates the lock with the specific file /// handle used to acquire the lock. If the same file is opened again with a /// different handle (even within the same process), attempts to write to the /// locked region using the new handle will fail. To ensure successful writes /// after locking, use the same [RandomAccessFile] object that acquired the /// lock for subsequent write operations. void lockSync([ FileLock mode = FileLock.exclusive, int start = 0, int end = -1, ]); /// Unlocks the file or part of the file. /// /// Unlocks the byte range from [start] to [end] of the file, with /// the byte at position `end` not included. If no arguments are /// specified, the full file is unlocked, If only `start` is /// specified the file is unlocked from byte position `start` to the /// end of the file. /// /// *NOTE* file locking does have slight differences in behavior across /// platforms: /// /// See [lock] for more details. Future unlock([int start = 0, int end = -1]); /// Synchronously unlocks the file or part of the file. /// /// Unlocks the byte range from [start] to [end] of the file, with /// the byte at position `end` not included. If no arguments are /// specified, the full file is unlocked, If only `start` is /// specified the file is unlocked from byte position `start` to the /// end of the file. /// /// *NOTE* file locking does have slight differences in behavior across /// platforms: /// /// See [lockSync] for more details. void unlockSync([int start = 0, int end = -1]); /// Returns a human-readable string for this random access file. String toString(); /// The path of the file underlying this random access file. String get path; } /// Exception thrown when a file operation fails. @pragma("vm:entry-point") class FileSystemException implements IOException { /// Message describing the error. /// /// The message does not include any detailed information from /// the underlying OS error. Check [osError] for that information. final String message; /// The file system path on which the error occurred. /// /// Can be `null` if the exception does not relate directly /// to a file system path. final String? path; /// The underlying OS error. /// /// Can be `null` if the exception is not raised due to an OS error. final OSError? osError; /// Creates a new file system exception with optional parts. /// /// Creates an exception with [FileSystemException.message], /// [FileSystemException.path] and [FileSystemException.osError] /// values take from the optional parameters of the same name. /// /// The [message] and [path] path defaults to empty strings if omitted, /// and [osError] defaults to `null`. const FileSystemException([this.message = "", this.path = "", this.osError]); /// Create a new file system exception based on an [OSError.errorCode]. /// /// For example, if `errorCode == 2` then a [PathNotFoundException] /// will be returned. @pragma("vm:entry-point") factory FileSystemException._fromOSError( OSError err, String message, String path, ) { if (Platform.isWindows) { switch (err.errorCode) { case _errorAccessDenied: case _errorCurrentDirectory: case _errorWriteProtect: case _errorBadLength: case _errorSharingViolation: case _errorLockViolation: case _errorNetworkAccessDenied: case _errorDriveLocked: return PathAccessException(path, err, message); case _errorFileExists: case _errorAlreadyExists: return PathExistsException(path, err, message); case _errorFileNotFound: case _errorPathNotFound: case _errorInvalidDrive: case _errorInvalidName: case _errorNoMoreFiles: case _errorBadNetpath: case _errorBadNetName: case _errorBadPathName: case _errorFilenameExedRange: return PathNotFoundException(path, err, message); default: return FileSystemException(message, path, err); } } else { switch (err.errorCode) { case _ePerm: case _eAccess: return PathAccessException(path, err, message); case _eExist: return PathExistsException(path, err, message); case _eNoEnt: return PathNotFoundException(path, err, message); default: return FileSystemException(message, path, err); } } } String _toStringHelper(String className) { StringBuffer sb = StringBuffer(); sb.write(className); if (message.isNotEmpty) { sb.write(": $message"); if (path != null) { sb.write(", path = '$path'"); } if (osError != null) { sb.write(" ($osError)"); } } else if (osError != null) { sb.write(": $osError"); if (path != null) { sb.write(", path = '$path'"); } } else if (path != null) { sb.write(": $path"); } return sb.toString(); } String toString() { return _toStringHelper("FileSystemException"); } } /// Exception thrown when a file operation fails because the necessary access /// rights are not available. @Since("2.19") class PathAccessException extends FileSystemException { const PathAccessException(String path, OSError osError, [String message = ""]) : super(message, path, osError); String toString() => _toStringHelper("PathAccessException"); } /// Exception thrown when a file operation fails because the target path /// already exists. @Since("2.19") class PathExistsException extends FileSystemException { const PathExistsException(String path, OSError osError, [String message = ""]) : super(message, path, osError); String toString() => _toStringHelper("PathExistsException"); } /// Exception thrown when a file operation fails because a file or /// directory does not exist. @Since("2.19") class PathNotFoundException extends FileSystemException { const PathNotFoundException( String path, OSError osError, [ String message = "", ]) : super(message, path, osError); String toString() => _toStringHelper("PathNotFoundException"); } /// The "read" end of an [Pipe] created by [Pipe.create]. /// /// The read stream will continue to listen until the "write" end of the /// pipe (i.e. [Pipe.write]) is closed. /// /// ```dart /// final pipe = await Pipe.create(); /// pipe.read.transform(utf8.decoder).listen((data) { /// print(data); /// }, onDone: () => print('Done')); /// ``` abstract interface class ReadPipe implements Stream> {} /// The "write" end of an [Pipe] created by [Pipe.create]. /// /// ```dart /// final pipe = await Pipe.create(); /// pipe.write.add("Hello World!".codeUnits); /// await pipe.write.flush(); /// await pipe.write.close(); /// ``` abstract interface class WritePipe implements IOSink {} /// An anonymous pipe that can be used to send data in a single direction i.e. /// data written to [write] can be read using [read]. /// /// On macOS and Linux (excluding Android), either the [read] or [write] /// portion of the pipe can be transmitted to another process and used for /// interprocess communication. /// /// For example: /// ```dart /// final pipe = await Pipe.create(); /// final socket = await RawSocket.connect(address, 0); /// socket.sendMessage([ /// SocketControlMessage.fromHandles( /// [ResourceHandle.fromReadPipe(pipe.read)]) /// ], 'Hello'.codeUnits); /// pipe.write.add('Hello over pipe!'.codeUnits); /// await pipe.write.flush(); /// await pipe.write.close(); /// ``` abstract interface class Pipe { /// The read end of the [Pipe]. ReadPipe get read; /// The write end of the [Pipe]. WritePipe get write; // Create an anonymous pipe. static Future create() { return _Pipe.create(); } /// Synchronously creates an anonymous pipe. factory Pipe.createSync() { return _Pipe.createSync(); } }