mirror of
https://github.com/flutter/packages.git
synced 2025-07-01 15:23:25 +08:00
[cross_file] Move from flutter/plugins. (#305)
This commit is contained in:
34
packages/cross_file/CHANGELOG.md
Normal file
34
packages/cross_file/CHANGELOG.md
Normal file
@ -0,0 +1,34 @@
|
||||
## 0.3.1+1
|
||||
|
||||
* Rehomed to `flutter/packages` repository.
|
||||
|
||||
## 0.3.1
|
||||
|
||||
* Fix nullability of `XFileBase`'s `path` and `name` to match the
|
||||
implementations to avoid potential analyzer issues.
|
||||
|
||||
## 0.3.0
|
||||
|
||||
* Migrated package to null-safety.
|
||||
* **breaking change** According to our unit tests, the API should be backwards-compatible. Some relevant changes were made, however:
|
||||
* Web: `lastModified` returns the epoch time as a default value, to maintain the `Future<DateTime>` return type (and not `null`)
|
||||
|
||||
## 0.2.1
|
||||
|
||||
* Prepare for breaking `package:http` change.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
* **breaking change** Make sure the `saveTo` method returns a `Future` so it can be awaited and users are sure the file has been written to disk.
|
||||
|
||||
## 0.1.0+2
|
||||
|
||||
* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276))
|
||||
|
||||
## 0.1.0+1
|
||||
|
||||
* Update Flutter SDK constraint.
|
||||
|
||||
## 0.1.0
|
||||
|
||||
* Initial open-source release.
|
25
packages/cross_file/LICENSE
Normal file
25
packages/cross_file/LICENSE
Normal file
@ -0,0 +1,25 @@
|
||||
Copyright 2020 The Flutter Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
34
packages/cross_file/README.md
Normal file
34
packages/cross_file/README.md
Normal file
@ -0,0 +1,34 @@
|
||||
# cross_file
|
||||
|
||||
An abstraction to allow working with files across multiple platforms.
|
||||
|
||||
# Usage
|
||||
|
||||
Import `package:cross/cross_info.dart`, instantiate a `CrossFile`
|
||||
using a path or byte array and use its methods and properties to
|
||||
access the file and its metadata.
|
||||
|
||||
Example:
|
||||
|
||||
```dart
|
||||
import 'package:cross_file/cross_file.dart';
|
||||
|
||||
final file = CrossFile('assets/hello.txt');
|
||||
|
||||
print('File information:');
|
||||
print('- Path: ${file.path}');
|
||||
print('- Name: ${file.name}');
|
||||
print('- MIME type: ${file.mimeType}');
|
||||
|
||||
final fileContent = await file.readAsString();
|
||||
print('Content of the file: ${fileContent}'); // e.g. "Moto G (4)"
|
||||
```
|
||||
|
||||
You will find links to the API docs on the [pub page](https://pub.dev/packages/cross_file).
|
||||
|
||||
## Getting Started
|
||||
|
||||
For help getting started with Flutter, view our online
|
||||
[documentation](http://flutter.io/).
|
||||
|
||||
For help on editing plugin code, view the [documentation](https://flutter.io/platform-plugins/#edit-code).
|
5
packages/cross_file/lib/cross_file.dart
Normal file
5
packages/cross_file/lib/cross_file.dart
Normal file
@ -0,0 +1,5 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
export 'src/x_file.dart';
|
87
packages/cross_file/lib/src/types/base.dart
Normal file
87
packages/cross_file/lib/src/types/base.dart
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
/// The interface for a CrossFile.
|
||||
///
|
||||
/// A CrossFile is a container that wraps the path of a selected
|
||||
/// file by the user and (in some platforms, like web) the bytes
|
||||
/// with the contents of the file.
|
||||
///
|
||||
/// This class is a very limited subset of dart:io [File], so all
|
||||
/// the methods should seem familiar.
|
||||
abstract class XFileBase {
|
||||
/// Construct a CrossFile
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
XFileBase(String? path);
|
||||
|
||||
/// Save the CrossFile at the indicated file path.
|
||||
Future<void> saveTo(String path) {
|
||||
throw UnimplementedError('saveTo has not been implemented.');
|
||||
}
|
||||
|
||||
/// Get the path of the picked file.
|
||||
///
|
||||
/// This should only be used as a backwards-compatibility clutch
|
||||
/// for mobile apps, or cosmetic reasons only (to show the user
|
||||
/// the path they've picked).
|
||||
///
|
||||
/// Accessing the data contained in the picked file by its path
|
||||
/// is platform-dependant (and won't work on web), so use the
|
||||
/// byte getters in the CrossFile instance instead.
|
||||
String get path {
|
||||
throw UnimplementedError('.path has not been implemented.');
|
||||
}
|
||||
|
||||
/// The name of the file as it was selected by the user in their device.
|
||||
///
|
||||
/// Use only for cosmetic reasons, do not try to use this as a path.
|
||||
String get name {
|
||||
throw UnimplementedError('.name has not been implemented.');
|
||||
}
|
||||
|
||||
/// For web, it may be necessary for a file to know its MIME type.
|
||||
String? get mimeType {
|
||||
throw UnimplementedError('.mimeType has not been implemented.');
|
||||
}
|
||||
|
||||
/// Get the length of the file. Returns a `Future<int>` that completes with the length in bytes.
|
||||
Future<int> length() {
|
||||
throw UnimplementedError('.length() has not been implemented.');
|
||||
}
|
||||
|
||||
/// Synchronously read the entire file contents as a string using the given [Encoding].
|
||||
///
|
||||
/// By default, `encoding` is [utf8].
|
||||
///
|
||||
/// Throws Exception if the operation fails.
|
||||
Future<String> readAsString({Encoding encoding = utf8}) {
|
||||
throw UnimplementedError('readAsString() has not been implemented.');
|
||||
}
|
||||
|
||||
/// Synchronously read the entire file contents as a list of bytes.
|
||||
///
|
||||
/// Throws Exception if the operation fails.
|
||||
Future<Uint8List> readAsBytes() {
|
||||
throw UnimplementedError('readAsBytes() has not been implemented.');
|
||||
}
|
||||
|
||||
/// Create 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 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<Uint8List> openRead([int? start, int? end]) {
|
||||
throw UnimplementedError('openRead() has not been implemented.');
|
||||
}
|
||||
|
||||
/// Get the last-modified time for the CrossFile
|
||||
Future<DateTime> lastModified() {
|
||||
throw UnimplementedError('openRead() has not been implemented.');
|
||||
}
|
||||
}
|
143
packages/cross_file/lib/src/types/html.dart
Normal file
143
packages/cross_file/lib/src/types/html.dart
Normal file
@ -0,0 +1,143 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:html';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../web_helpers/web_helpers.dart';
|
||||
import './base.dart';
|
||||
|
||||
/// A CrossFile that works on web.
|
||||
///
|
||||
/// It wraps the bytes of a selected file.
|
||||
class XFile extends XFileBase {
|
||||
/// Construct a CrossFile object from its ObjectUrl.
|
||||
///
|
||||
/// Optionally, this can be initialized with `bytes` and `length`
|
||||
/// so no http requests are performed to retrieve files later.
|
||||
///
|
||||
/// `name` needs to be passed from the outside, since we only have
|
||||
/// access to it while we create the ObjectUrl.
|
||||
XFile(
|
||||
this.path, {
|
||||
this.mimeType,
|
||||
String? name,
|
||||
int? length,
|
||||
Uint8List? bytes,
|
||||
DateTime? lastModified,
|
||||
@visibleForTesting CrossFileTestOverrides? overrides,
|
||||
}) : _data = bytes,
|
||||
_length = length,
|
||||
_overrides = overrides,
|
||||
_lastModified = lastModified ?? DateTime.fromMillisecondsSinceEpoch(0),
|
||||
name = name ?? '',
|
||||
super(path);
|
||||
|
||||
/// Construct an CrossFile from its data
|
||||
XFile.fromData(
|
||||
Uint8List bytes, {
|
||||
this.mimeType,
|
||||
String? name,
|
||||
int? length,
|
||||
DateTime? lastModified,
|
||||
String? path,
|
||||
@visibleForTesting CrossFileTestOverrides? overrides,
|
||||
}) : _data = bytes,
|
||||
_length = length,
|
||||
_overrides = overrides,
|
||||
_lastModified = lastModified ?? DateTime.fromMillisecondsSinceEpoch(0),
|
||||
name = name ?? '',
|
||||
super(path) {
|
||||
if (path == null) {
|
||||
final Blob blob = (mimeType == null)
|
||||
? Blob(<dynamic>[bytes])
|
||||
: Blob(<dynamic>[bytes], mimeType);
|
||||
this.path = Url.createObjectUrl(blob);
|
||||
} else {
|
||||
this.path = path;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
final String? mimeType;
|
||||
@override
|
||||
final String name;
|
||||
@override
|
||||
late String path;
|
||||
|
||||
final Uint8List? _data;
|
||||
final int? _length;
|
||||
final DateTime? _lastModified;
|
||||
|
||||
late Element _target;
|
||||
|
||||
final CrossFileTestOverrides? _overrides;
|
||||
|
||||
bool get _hasTestOverrides => _overrides != null;
|
||||
|
||||
@override
|
||||
Future<DateTime> lastModified() async =>
|
||||
Future<DateTime>.value(_lastModified);
|
||||
|
||||
Future<Uint8List> get _bytes async {
|
||||
if (_data != null) {
|
||||
return Future<Uint8List>.value(UnmodifiableUint8ListView(_data!));
|
||||
}
|
||||
|
||||
// We can force 'response' to be a byte buffer by passing responseType:
|
||||
final ByteBuffer? response =
|
||||
(await HttpRequest.request(path, responseType: 'arraybuffer')).response;
|
||||
|
||||
return response?.asUint8List() ?? Uint8List(0);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> length() async => _length ?? (await _bytes).length;
|
||||
|
||||
@override
|
||||
Future<String> readAsString({Encoding encoding = utf8}) async {
|
||||
return encoding.decode(await _bytes);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> readAsBytes() async =>
|
||||
Future<Uint8List>.value(await _bytes);
|
||||
|
||||
@override
|
||||
Stream<Uint8List> openRead([int? start, int? end]) async* {
|
||||
final Uint8List bytes = await _bytes;
|
||||
yield bytes.sublist(start ?? 0, end ?? bytes.length);
|
||||
}
|
||||
|
||||
/// Saves the data of this CrossFile at the location indicated by path.
|
||||
/// For the web implementation, the path variable is ignored.
|
||||
@override
|
||||
Future<void> saveTo(String path) async {
|
||||
// Create a DOM container where we can host the anchor.
|
||||
_target = ensureInitialized('__x_file_dom_element');
|
||||
|
||||
// Create an <a> tag with the appropriate download attributes and click it
|
||||
// May be overridden with CrossFileTestOverrides
|
||||
final AnchorElement element = _hasTestOverrides
|
||||
? _overrides!.createAnchorElement(this.path, name) as AnchorElement
|
||||
: createAnchorElement(this.path, name);
|
||||
|
||||
// Clear the children in our container so we can add an element to click
|
||||
_target.children.clear();
|
||||
addElementToContainerAndClick(_target, element);
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides some functions to allow testing
|
||||
@visibleForTesting
|
||||
class CrossFileTestOverrides {
|
||||
/// Default constructor for overrides
|
||||
CrossFileTestOverrides({required this.createAnchorElement});
|
||||
|
||||
/// For overriding the creation of the file input element.
|
||||
Element Function(String href, String suggestedName) createAnchorElement;
|
||||
}
|
60
packages/cross_file/lib/src/types/interface.dart
Normal file
60
packages/cross_file/lib/src/types/interface.dart
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:typed_data';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import './base.dart';
|
||||
|
||||
// ignore_for_file: avoid_unused_constructor_parameters
|
||||
|
||||
/// A CrossFile is a cross-platform, simplified File abstraction.
|
||||
///
|
||||
/// It wraps the bytes of a selected file, and its (platform-dependant) path.
|
||||
class XFile extends XFileBase {
|
||||
/// Construct a CrossFile object from its path.
|
||||
///
|
||||
/// Optionally, this can be initialized with `bytes` and `length`
|
||||
/// so no http requests are performed to retrieve data later.
|
||||
///
|
||||
/// `name` may be passed from the outside, for those cases where the effective
|
||||
/// `path` of the file doesn't match what the user sees when selecting it
|
||||
/// (like in web)
|
||||
XFile(
|
||||
String path, {
|
||||
String? mimeType,
|
||||
String? name,
|
||||
int? length,
|
||||
Uint8List? bytes,
|
||||
DateTime? lastModified,
|
||||
@visibleForTesting CrossFileTestOverrides? overrides,
|
||||
}) : super(path) {
|
||||
throw UnimplementedError(
|
||||
'CrossFile is not available in your current platform.');
|
||||
}
|
||||
|
||||
/// Construct a CrossFile object from its data
|
||||
XFile.fromData(
|
||||
Uint8List bytes, {
|
||||
String? mimeType,
|
||||
String? name,
|
||||
int? length,
|
||||
DateTime? lastModified,
|
||||
String? path,
|
||||
@visibleForTesting CrossFileTestOverrides? overrides,
|
||||
}) : super(path) {
|
||||
throw UnimplementedError(
|
||||
'CrossFile is not available in your current platform.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides some functions of CrossFile for testing purposes
|
||||
@visibleForTesting
|
||||
class CrossFileTestOverrides {
|
||||
/// Default constructor for overrides
|
||||
CrossFileTestOverrides({required this.createAnchorElement});
|
||||
|
||||
/// For overriding the creation of the file input element.
|
||||
dynamic Function(String href, String suggestedName) createAnchorElement;
|
||||
}
|
119
packages/cross_file/lib/src/types/io.dart
Normal file
119
packages/cross_file/lib/src/types/io.dart
Normal file
@ -0,0 +1,119 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import './base.dart';
|
||||
|
||||
// ignore_for_file: avoid_unused_constructor_parameters
|
||||
|
||||
/// A CrossFile backed by a dart:io File.
|
||||
class XFile extends XFileBase {
|
||||
/// Construct a CrossFile object backed by a dart:io File.
|
||||
XFile(
|
||||
String path, {
|
||||
this.mimeType,
|
||||
String? name,
|
||||
int? length,
|
||||
Uint8List? bytes,
|
||||
DateTime? lastModified,
|
||||
}) : _file = File(path),
|
||||
_bytes = null,
|
||||
_lastModified = lastModified,
|
||||
super(path);
|
||||
|
||||
/// Construct an CrossFile from its data
|
||||
XFile.fromData(
|
||||
Uint8List bytes, {
|
||||
this.mimeType,
|
||||
String? path,
|
||||
String? name,
|
||||
int? length,
|
||||
DateTime? lastModified,
|
||||
}) : _bytes = bytes,
|
||||
_file = File(path ?? ''),
|
||||
_length = length,
|
||||
_lastModified = lastModified,
|
||||
super(path) {
|
||||
if (length == null) {
|
||||
_length = bytes.length;
|
||||
}
|
||||
}
|
||||
|
||||
final File _file;
|
||||
@override
|
||||
final String? mimeType;
|
||||
final DateTime? _lastModified;
|
||||
int? _length;
|
||||
|
||||
final Uint8List? _bytes;
|
||||
|
||||
@override
|
||||
Future<DateTime> lastModified() {
|
||||
if (_lastModified != null) {
|
||||
return Future<DateTime>.value(_lastModified);
|
||||
}
|
||||
// ignore: avoid_slow_async_io
|
||||
return _file.lastModified();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveTo(String path) async {
|
||||
final File fileToSave = File(path);
|
||||
await fileToSave.writeAsBytes(_bytes ?? (await readAsBytes()));
|
||||
await fileToSave.create();
|
||||
}
|
||||
|
||||
@override
|
||||
String get path {
|
||||
return _file.path;
|
||||
}
|
||||
|
||||
@override
|
||||
String get name {
|
||||
return _file.path.split(Platform.pathSeparator).last;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> length() {
|
||||
if (_length != null) {
|
||||
return Future<int>.value(_length);
|
||||
}
|
||||
return _file.length();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> readAsString({Encoding encoding = utf8}) {
|
||||
if (_bytes != null) {
|
||||
return Future<String>.value(String.fromCharCodes(_bytes!));
|
||||
}
|
||||
return _file.readAsString(encoding: encoding);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> readAsBytes() {
|
||||
if (_bytes != null) {
|
||||
return Future<Uint8List>.value(_bytes);
|
||||
}
|
||||
return _file.readAsBytes();
|
||||
}
|
||||
|
||||
Stream<Uint8List> _getBytes(int? start, int? end) async* {
|
||||
final Uint8List bytes = _bytes!;
|
||||
yield bytes.sublist(start ?? 0, end ?? bytes.length);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<Uint8List> openRead([int? start, int? end]) {
|
||||
if (_bytes != null) {
|
||||
return _getBytes(start, end);
|
||||
} else {
|
||||
return _file
|
||||
.openRead(start ?? 0, end)
|
||||
.map((List<int> chunk) => Uint8List.fromList(chunk));
|
||||
}
|
||||
}
|
||||
}
|
38
packages/cross_file/lib/src/web_helpers/web_helpers.dart
Normal file
38
packages/cross_file/lib/src/web_helpers/web_helpers.dart
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:html';
|
||||
|
||||
/// Create anchor element with download attribute
|
||||
AnchorElement createAnchorElement(String href, String? suggestedName) {
|
||||
final AnchorElement element = AnchorElement(href: href);
|
||||
|
||||
if (suggestedName == null) {
|
||||
element.download = 'download';
|
||||
} else {
|
||||
element.download = suggestedName;
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/// Add an element to a container and click it
|
||||
void addElementToContainerAndClick(Element container, Element element) {
|
||||
// Add the element and click it
|
||||
// All previous elements will be removed before adding the new one
|
||||
container.children.add(element);
|
||||
element.click();
|
||||
}
|
||||
|
||||
/// Initializes a DOM container where we can host elements.
|
||||
Element ensureInitialized(String id) {
|
||||
Element? target = querySelector('#$id');
|
||||
if (target == null) {
|
||||
final Element targetElement = Element.tag('flt-x-file')..id = id;
|
||||
|
||||
querySelector('body')!.children.add(targetElement);
|
||||
target = targetElement;
|
||||
}
|
||||
return target;
|
||||
}
|
7
packages/cross_file/lib/src/x_file.dart
Normal file
7
packages/cross_file/lib/src/x_file.dart
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
export 'types/interface.dart'
|
||||
if (dart.library.html) 'types/html.dart'
|
||||
if (dart.library.io) 'types/io.dart';
|
18
packages/cross_file/pubspec.yaml
Normal file
18
packages/cross_file/pubspec.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
name: cross_file
|
||||
description: An abstraction to allow working with files across multiple platforms.
|
||||
repository: https://github.com/flutter/packages/tree/master/packages/cross_file
|
||||
version: 0.3.1+1
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
meta: ^1.3.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
pedantic: ^1.10.0
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
flutter: ">=1.22.0"
|
1
packages/cross_file/test/assets/hello.txt
Normal file
1
packages/cross_file/test/assets/hello.txt
Normal file
@ -0,0 +1 @@
|
||||
Hello, world!
|
108
packages/cross_file/test/x_file_html_test.dart
Normal file
108
packages/cross_file/test/x_file_html_test.dart
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright 2020 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
@TestOn('chrome') // Uses web-only Flutter SDK
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:html' as html;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:cross_file/cross_file.dart';
|
||||
|
||||
const String expectedStringContents = 'Hello, world!';
|
||||
final Uint8List bytes = Uint8List.fromList(utf8.encode(expectedStringContents));
|
||||
final html.File textFile = html.File(<Object>[bytes], 'hello.txt');
|
||||
final String textFileUrl = html.Url.createObjectUrl(textFile);
|
||||
|
||||
void main() {
|
||||
group('Create with an objectUrl', () {
|
||||
final XFile file = XFile(textFileUrl);
|
||||
|
||||
test('Can be read as a string', () async {
|
||||
expect(await file.readAsString(), equals(expectedStringContents));
|
||||
});
|
||||
test('Can be read as bytes', () async {
|
||||
expect(await file.readAsBytes(), equals(bytes));
|
||||
});
|
||||
|
||||
test('Can be read as a stream', () async {
|
||||
expect(await file.openRead().first, equals(bytes));
|
||||
});
|
||||
|
||||
test('Stream can be sliced', () async {
|
||||
expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5)));
|
||||
});
|
||||
});
|
||||
|
||||
group('Create from data', () {
|
||||
final XFile file = XFile.fromData(bytes);
|
||||
|
||||
test('Can be read as a string', () async {
|
||||
expect(await file.readAsString(), equals(expectedStringContents));
|
||||
});
|
||||
test('Can be read as bytes', () async {
|
||||
expect(await file.readAsBytes(), equals(bytes));
|
||||
});
|
||||
|
||||
test('Can be read as a stream', () async {
|
||||
expect(await file.openRead().first, equals(bytes));
|
||||
});
|
||||
|
||||
test('Stream can be sliced', () async {
|
||||
expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5)));
|
||||
});
|
||||
});
|
||||
|
||||
group('saveTo(..)', () {
|
||||
const String crossFileDomElementId = '__x_file_dom_element';
|
||||
|
||||
group('CrossFile saveTo(..)', () {
|
||||
test('creates a DOM container', () async {
|
||||
final XFile file = XFile.fromData(bytes);
|
||||
|
||||
await file.saveTo('');
|
||||
|
||||
final html.Element? container =
|
||||
html.querySelector('#$crossFileDomElementId');
|
||||
|
||||
expect(container, isNotNull);
|
||||
});
|
||||
|
||||
test('create anchor element', () async {
|
||||
final XFile file = XFile.fromData(bytes, name: textFile.name);
|
||||
|
||||
await file.saveTo('path');
|
||||
|
||||
final html.Element? container =
|
||||
html.querySelector('#$crossFileDomElementId');
|
||||
final html.AnchorElement element = container?.children
|
||||
.firstWhere((html.Element element) => element.tagName == 'A')
|
||||
as html.AnchorElement;
|
||||
|
||||
// if element is not found, the `firstWhere` call will throw StateError.
|
||||
expect(element.href, file.path);
|
||||
expect(element.download, file.name);
|
||||
});
|
||||
|
||||
test('anchor element is clicked', () async {
|
||||
final html.AnchorElement mockAnchor = html.AnchorElement();
|
||||
|
||||
final CrossFileTestOverrides overrides = CrossFileTestOverrides(
|
||||
createAnchorElement: (_, __) => mockAnchor,
|
||||
);
|
||||
|
||||
final XFile file =
|
||||
XFile.fromData(bytes, name: textFile.name, overrides: overrides);
|
||||
|
||||
bool clicked = false;
|
||||
mockAnchor.onClick.listen((html.MouseEvent event) => clicked = true);
|
||||
|
||||
await file.saveTo('path');
|
||||
|
||||
expect(clicked, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
90
packages/cross_file/test/x_file_io_test.dart
Normal file
90
packages/cross_file/test/x_file_io_test.dart
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright 2020 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
@TestOn('vm') // Uses dart:io
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:cross_file/cross_file.dart';
|
||||
|
||||
final String pathPrefix =
|
||||
Directory.current.path.endsWith('test') ? './assets/' : './test/assets/';
|
||||
final String path = pathPrefix + 'hello.txt';
|
||||
const String expectedStringContents = 'Hello, world!';
|
||||
final Uint8List bytes = Uint8List.fromList(utf8.encode(expectedStringContents));
|
||||
final File textFile = File(path);
|
||||
final String textFilePath = textFile.path;
|
||||
|
||||
void main() {
|
||||
group('Create with a path', () {
|
||||
final XFile file = XFile(textFilePath);
|
||||
|
||||
test('Can be read as a string', () async {
|
||||
expect(await file.readAsString(), equals(expectedStringContents));
|
||||
});
|
||||
test('Can be read as bytes', () async {
|
||||
expect(await file.readAsBytes(), equals(bytes));
|
||||
});
|
||||
|
||||
test('Can be read as a stream', () async {
|
||||
expect(await file.openRead().first, equals(bytes));
|
||||
});
|
||||
|
||||
test('Stream can be sliced', () async {
|
||||
expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5)));
|
||||
});
|
||||
|
||||
test('saveTo(..) creates file', () async {
|
||||
final File removeBeforeTest = File(pathPrefix + 'newFilePath.txt');
|
||||
if (removeBeforeTest.existsSync()) {
|
||||
await removeBeforeTest.delete();
|
||||
}
|
||||
|
||||
await file.saveTo(pathPrefix + 'newFilePath.txt');
|
||||
final File newFile = File(pathPrefix + 'newFilePath.txt');
|
||||
|
||||
expect(newFile.existsSync(), isTrue);
|
||||
expect(newFile.readAsStringSync(), 'Hello, world!');
|
||||
|
||||
await newFile.delete();
|
||||
});
|
||||
});
|
||||
|
||||
group('Create with data', () {
|
||||
final XFile file = XFile.fromData(bytes);
|
||||
|
||||
test('Can be read as a string', () async {
|
||||
expect(await file.readAsString(), equals(expectedStringContents));
|
||||
});
|
||||
test('Can be read as bytes', () async {
|
||||
expect(await file.readAsBytes(), equals(bytes));
|
||||
});
|
||||
|
||||
test('Can be read as a stream', () async {
|
||||
expect(await file.openRead().first, equals(bytes));
|
||||
});
|
||||
|
||||
test('Stream can be sliced', () async {
|
||||
expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5)));
|
||||
});
|
||||
|
||||
test('Function saveTo(..) creates file', () async {
|
||||
final File removeBeforeTest = File(pathPrefix + 'newFileData.txt');
|
||||
if (removeBeforeTest.existsSync()) {
|
||||
await removeBeforeTest.delete();
|
||||
}
|
||||
|
||||
await file.saveTo(pathPrefix + 'newFileData.txt');
|
||||
final File newFile = File(pathPrefix + 'newFileData.txt');
|
||||
|
||||
expect(newFile.existsSync(), isTrue);
|
||||
expect(newFile.readAsStringSync(), 'Hello, world!');
|
||||
|
||||
await newFile.delete();
|
||||
});
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user