| // 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 = const FileMode._internal(0); |
| @Deprecated("Use read instead") |
| static const READ = read; |
| |
| /// 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 = const FileMode._internal(1); |
| @Deprecated("Use write instead") |
| static const WRITE = write; |
| |
| /// 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 = const FileMode._internal(2); |
| @Deprecated("Use append instead") |
| static const APPEND = append; |
| |
| /// 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 = const FileMode._internal(3); |
| @Deprecated("Use writeOnly instead") |
| static const WRITE_ONLY = writeOnly; |
| |
| /// 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 = const FileMode._internal(4); |
| @Deprecated("Use writeOnlyAppend instead") |
| static const WRITE_ONLY_APPEND = writeOnlyAppend; |
| |
| final int _mode; |
| |
| const FileMode._internal(this._mode); |
| } |
| |
| /// The mode for opening a file only for reading. |
| @Deprecated("Use FileMode.read instead") |
| const READ = FileMode.read; |
| |
| /// The 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. |
| @Deprecated("Use FileMode.write instead") |
| const WRITE = FileMode.write; |
| |
| /// The mode for opening a file for reading and writing to the |
| /// end of it. The file is created if it does not already exist. |
| @Deprecated("Use FileMode.append instead") |
| const APPEND = FileMode.append; |
| |
| /// 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. |
| @Deprecated("Use FileMode.writeOnly instead") |
| const WRITE_ONLY = FileMode.writeOnly; |
| |
| /// Mode for opening a file for writing *only* to the |
| /// end of it. The file is created if it does not already exist. |
| @Deprecated("Use FileMode.writeOnlyAppend instead") |
| const WRITE_ONLY_APPEND = FileMode.writeOnlyAppend; |
| |
| /// Type of lock when requesting a lock on a file. |
| class FileLock { |
| /// Shared file lock. |
| static const shared = const FileLock._internal(1); |
| @Deprecated("Use shared instead") |
| static const SHARED = shared; |
| |
| /// Exclusive file lock. |
| static const exclusive = const FileLock._internal(2); |
| @Deprecated("Use exclusive instead") |
| static const EXCLUSIVE = exclusive; |
| |
| /// Blocking shared file lock. |
| static const blockingShared = const FileLock._internal(3); |
| @Deprecated("Use blockingShared instead") |
| static const BLOCKING_SHARED = blockingShared; |
| |
| /// Blocking exclusive file lock. |
| static const blockingExclusive = const FileLock._internal(4); |
| @Deprecated("Use blockingExclusive instead") |
| static const BLOCKING_EXCLUSIVE = blockingExclusive; |
| |
| 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<String> 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() { |
| /// var file = File('file.txt'); |
| /// var sink = file.openWrite(); |
| /// sink.write('FILE ACCESSED ${DateTime.now()}\n'); |
| /// |
| /// // Close the IOSink to free system resources. |
| /// 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. |
| /// |
| /// ## 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 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 new _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) => new 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 new _File.fromRawPath(rawPath); |
| } |
| |
| /// Creates the file. |
| /// |
| /// Returns a `Future<File>` 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. |
| /// |
| /// Existing files are left untouched by [create]. Calling [create] on an |
| /// existing file might fail if there are restrictive permissions on |
| /// the file. |
| /// |
| /// Completes the future with a [FileSystemException] if the operation fails. |
| Future<File> create({bool recursive = false}); |
| |
| /// Synchronously creates the file. |
| /// |
| /// Existing files are left untouched by [createSync]. |
| /// Calling [createSync] on an existing file might fail |
| /// if there are restrictive permissions on 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. |
| /// |
| /// Throws a [FileSystemException] if the operation fails. |
| void createSync({bool recursive = false}); |
| |
| /// Renames this file. |
| /// |
| /// Returns a `Future<File>` 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<File> 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, that file is |
| /// removed first. If [newPath] identifies an existing directory, the |
| /// operation fails and the future completes with an exception. |
| Future<File> 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, that file is |
| /// removed first. If [newPath] identifies an existing directory the |
| /// operation fails and an exception is thrown. |
| File renameSync(String newPath); |
| |
| /// Copies this file. |
| /// |
| /// If [newPath] is a relative path, it is resolved against |
| /// the current working directory ([Directory.current]). |
| /// |
| /// Returns a `Future<File>` 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<File> 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<int>` that completes with the length in bytes. |
| Future<int> 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<DateTime>` 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<DateTime> 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<DateTime>` 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<DateTime> 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<RandomAccessFile>` 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. |
| Future<RandomAccessFile> 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. |
| Stream<List<int>> 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. |
| IOSink openWrite({FileMode mode = FileMode.write, Encoding encoding = utf8}); |
| |
| /// Reads the entire file contents as a list of bytes. |
| /// |
| /// Returns a `Future<Uint8List>` that completes with the list of bytes that |
| /// is the contents of the file. |
| Future<Uint8List> 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<String>` that completes with the string once |
| /// the file contents has been read. |
| Future<String> 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<List<String>>` that completes with the lines |
| /// once the file contents has been read. |
| Future<List<String>> 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<String> 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<File>` 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. |
| /// |
| /// If the argument [flush] is set to `true`, the data written will be |
| /// flushed to the file system before the returned future completes. |
| Future<File> writeAsBytes(List<int> 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. |
| /// |
| /// 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<int> 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<File>` 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. |
| /// |
| Future<File> 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. |
| /// |
| /// 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 class RandomAccessFile { |
| /// Closes the file. |
| /// |
| /// Returns a [Future] that completes when it has been closed. |
| Future<void> close(); |
| |
| /// Synchronously closes the file. |
| /// |
| /// Throws a [FileSystemException] if the operation fails. |
| void closeSync(); |
| |
| /// Reads a byte from the file. |
| /// |
| /// Returns a `Future<int>` that completes with the byte, |
| /// or with -1 if end-of-file has been reached. |
| Future<int> 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. |
| Future<Uint8List> read(int count); |
| |
| /// Synchronously reads up to [count] bytes from a file |
| /// |
| /// Throws a [FileSystemException] if the operation fails. |
| Uint8List readSync(int count); |
| |
| /// Reads bytes into an existing [buffer]. |
| /// |
| /// Reads bytes and writes then into the 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<int> readInto(List<int> buffer, [int start = 0, int? end]); |
| |
| /// Synchronously reads into an existing [buffer]. |
| /// |
| /// Reads bytes and writes then into the 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<int> buffer, [int start = 0, int? end]); |
| |
| /// Writes a single byte to the file. |
| /// |
| /// Returns a `Future<RandomAccessFile>` that completes with this |
| /// random access file when the write completes. |
| Future<RandomAccessFile> 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<RandomAccessFile>` that completes with this |
| /// [RandomAccessFile] when the write completes. |
| Future<RandomAccessFile> writeFrom(List<int> 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<int> buffer, [int start = 0, int? end]); |
| |
| /// Writes a string to the file using the given [Encoding]. |
| /// |
| /// Returns a `Future<RandomAccessFile>` that completes with this |
| /// random access file when the write completes. |
| Future<RandomAccessFile> 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<int>` that completes with the position. |
| Future<int> 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<RandomAccessFile>` that completes with this |
| /// random access file when the position has been set. |
| Future<RandomAccessFile> 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<RandomAccessFile>` that completes with this |
| /// random access file when the truncation has been performed. |
| Future<RandomAccessFile> 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<int>` that completes with the length in bytes. |
| Future<int> 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<RandomAccessFile>` that completes with this |
| /// random access file when the flush operation completes. |
| Future<RandomAccessFile> 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". |
| Future<RandomAccessFile> 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". |
| /// |
| 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<RandomAccessFile> 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`. |
| @pragma("vm:entry-point") |
| const FileSystemException([this.message = "", this.path = "", this.osError]); |
| |
| String toString() { |
| StringBuffer sb = new StringBuffer(); |
| sb.write("FileSystemException"); |
| 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(); |
| } |
| } |