mirror of
https://github.com/flutter/packages.git
synced 2025-07-03 09:08:54 +08:00
This reverts commit ca9921196a7ae96edad91a9cd7b7d8fe9f5689ff.
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,12 +1,6 @@
|
|||||||
## 0.3.0-nullsafety
|
|
||||||
|
|
||||||
* 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
|
## 0.2.1
|
||||||
|
|
||||||
* Prepare for breaking `package:http` change.
|
* Prepare for breaking `package:http` change.
|
||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
|
|
||||||
@ -18,8 +12,8 @@
|
|||||||
|
|
||||||
## 0.1.0+1
|
## 0.1.0+1
|
||||||
|
|
||||||
* Update Flutter SDK constraint.
|
- Update Flutter SDK constraint.
|
||||||
|
|
||||||
## 0.1.0
|
## 0.1.0
|
||||||
|
|
||||||
* Initial open-source release.
|
- Initial open-source release
|
||||||
|
@ -15,7 +15,7 @@ import 'dart:typed_data';
|
|||||||
/// the methods should seem familiar.
|
/// the methods should seem familiar.
|
||||||
abstract class XFileBase {
|
abstract class XFileBase {
|
||||||
/// Construct a CrossFile
|
/// Construct a CrossFile
|
||||||
XFileBase(String? path);
|
XFileBase(String path);
|
||||||
|
|
||||||
/// Save the CrossFile at the indicated file path.
|
/// Save the CrossFile at the indicated file path.
|
||||||
Future<void> saveTo(String path) {
|
Future<void> saveTo(String path) {
|
||||||
@ -31,19 +31,19 @@ abstract class XFileBase {
|
|||||||
/// Accessing the data contained in the picked file by its path
|
/// Accessing the data contained in the picked file by its path
|
||||||
/// is platform-dependant (and won't work on web), so use the
|
/// is platform-dependant (and won't work on web), so use the
|
||||||
/// byte getters in the CrossFile instance instead.
|
/// byte getters in the CrossFile instance instead.
|
||||||
String? get path {
|
String get path {
|
||||||
throw UnimplementedError('.path has not been implemented.');
|
throw UnimplementedError('.path has not been implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The name of the file as it was selected by the user in their device.
|
/// 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.
|
/// Use only for cosmetic reasons, do not try to use this as a path.
|
||||||
String? get name {
|
String get name {
|
||||||
throw UnimplementedError('.name has not been implemented.');
|
throw UnimplementedError('.name has not been implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For web, it may be necessary for a file to know its MIME type.
|
/// For web, it may be necessary for a file to know its MIME type.
|
||||||
String? get mimeType {
|
String get mimeType {
|
||||||
throw UnimplementedError('.mimeType has not been implemented.');
|
throw UnimplementedError('.mimeType has not been implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ abstract class XFileBase {
|
|||||||
/// If `end` is present, only up to byte-index `end` will be read. Otherwise, until end of file.
|
/// 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.
|
/// 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]) {
|
Stream<Uint8List> openRead([int start, int end]) {
|
||||||
throw UnimplementedError('openRead() has not been implemented.');
|
throw UnimplementedError('openRead() has not been implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import 'dart:convert';
|
|||||||
import 'dart:html';
|
import 'dart:html';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:http/http.dart' as http show readBytes;
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
import './base.dart';
|
import './base.dart';
|
||||||
@ -15,17 +16,16 @@ import '../web_helpers/web_helpers.dart';
|
|||||||
///
|
///
|
||||||
/// It wraps the bytes of a selected file.
|
/// It wraps the bytes of a selected file.
|
||||||
class XFile extends XFileBase {
|
class XFile extends XFileBase {
|
||||||
late String path;
|
String path;
|
||||||
|
|
||||||
final String? mimeType;
|
final String mimeType;
|
||||||
final Uint8List? _data;
|
final Uint8List _data;
|
||||||
final int? _length;
|
final int _length;
|
||||||
final String name;
|
final String name;
|
||||||
final DateTime? _lastModified;
|
final DateTime _lastModified;
|
||||||
|
Element _target;
|
||||||
|
|
||||||
late Element _target;
|
final CrossFileTestOverrides _overrides;
|
||||||
|
|
||||||
final CrossFileTestOverrides? _overrides;
|
|
||||||
|
|
||||||
bool get _hasTestOverrides => _overrides != null;
|
bool get _hasTestOverrides => _overrides != null;
|
||||||
|
|
||||||
@ -39,58 +39,56 @@ class XFile extends XFileBase {
|
|||||||
XFile(
|
XFile(
|
||||||
this.path, {
|
this.path, {
|
||||||
this.mimeType,
|
this.mimeType,
|
||||||
String? name,
|
this.name,
|
||||||
int? length,
|
int length,
|
||||||
Uint8List? bytes,
|
Uint8List bytes,
|
||||||
DateTime? lastModified,
|
DateTime lastModified,
|
||||||
@visibleForTesting CrossFileTestOverrides? overrides,
|
@visibleForTesting CrossFileTestOverrides overrides,
|
||||||
}) : _data = bytes,
|
}) : _data = bytes,
|
||||||
_length = length,
|
_length = length,
|
||||||
_overrides = overrides,
|
_overrides = overrides,
|
||||||
_lastModified = lastModified ?? DateTime.fromMillisecondsSinceEpoch(0),
|
_lastModified = lastModified,
|
||||||
name = name ?? '',
|
|
||||||
super(path);
|
super(path);
|
||||||
|
|
||||||
/// Construct an CrossFile from its data
|
/// Construct an CrossFile from its data
|
||||||
XFile.fromData(
|
XFile.fromData(
|
||||||
Uint8List bytes, {
|
Uint8List bytes, {
|
||||||
this.mimeType,
|
this.mimeType,
|
||||||
String? name,
|
this.name,
|
||||||
int? length,
|
int length,
|
||||||
DateTime? lastModified,
|
DateTime lastModified,
|
||||||
String? path,
|
this.path,
|
||||||
@visibleForTesting CrossFileTestOverrides? overrides,
|
@visibleForTesting CrossFileTestOverrides overrides,
|
||||||
}) : _data = bytes,
|
}) : _data = bytes,
|
||||||
_length = length,
|
_length = length,
|
||||||
_overrides = overrides,
|
_overrides = overrides,
|
||||||
_lastModified = lastModified ?? DateTime.fromMillisecondsSinceEpoch(0),
|
_lastModified = lastModified,
|
||||||
name = name ?? '',
|
|
||||||
super(path) {
|
super(path) {
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
final blob = (mimeType == null) ? Blob([bytes]) : Blob([bytes], mimeType);
|
final blob = (mimeType == null) ? Blob([bytes]) : Blob([bytes], mimeType);
|
||||||
this.path = Url.createObjectUrl(blob);
|
this.path = Url.createObjectUrl(blob);
|
||||||
} else {
|
|
||||||
this.path = path;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<DateTime> lastModified() async => Future.value(_lastModified);
|
Future<DateTime> lastModified() async {
|
||||||
|
if (_lastModified != null) {
|
||||||
|
return Future.value(_lastModified);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Future<Uint8List> get _bytes async {
|
Future<Uint8List> get _bytes async {
|
||||||
if (_data != null) {
|
if (_data != null) {
|
||||||
return Future.value(UnmodifiableUint8ListView(_data!));
|
return Future.value(UnmodifiableUint8ListView(_data));
|
||||||
}
|
}
|
||||||
|
return http.readBytes(Uri.parse(path));
|
||||||
// We can force 'response' to be a byte buffer by passing responseType:
|
|
||||||
ByteBuffer? response =
|
|
||||||
(await HttpRequest.request(path, responseType: 'arraybuffer')).response;
|
|
||||||
|
|
||||||
return response?.asUint8List() ?? Uint8List(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> length() async => _length ?? (await _bytes).length;
|
Future<int> length() async {
|
||||||
|
return _length ?? (await _bytes).length;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> readAsString({Encoding encoding = utf8}) async {
|
Future<String> readAsString({Encoding encoding = utf8}) async {
|
||||||
@ -98,10 +96,12 @@ class XFile extends XFileBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> readAsBytes() async => Future.value(await _bytes);
|
Future<Uint8List> readAsBytes() async {
|
||||||
|
return Future.value(await _bytes);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<Uint8List> openRead([int? start, int? end]) async* {
|
Stream<Uint8List> openRead([int start, int end]) async* {
|
||||||
final bytes = await _bytes;
|
final bytes = await _bytes;
|
||||||
yield bytes.sublist(start ?? 0, end ?? bytes.length);
|
yield bytes.sublist(start ?? 0, end ?? bytes.length);
|
||||||
}
|
}
|
||||||
@ -114,9 +114,10 @@ class XFile extends XFileBase {
|
|||||||
|
|
||||||
// Create an <a> tag with the appropriate download attributes and click it
|
// Create an <a> tag with the appropriate download attributes and click it
|
||||||
// May be overridden with CrossFileTestOverrides
|
// May be overridden with CrossFileTestOverrides
|
||||||
final AnchorElement element = _hasTestOverrides
|
final AnchorElement element =
|
||||||
? _overrides!.createAnchorElement(this.path, this.name) as AnchorElement
|
(_hasTestOverrides && _overrides.createAnchorElement != null)
|
||||||
: createAnchorElement(this.path, this.name);
|
? _overrides.createAnchorElement(this.path, this.name)
|
||||||
|
: createAnchorElement(this.path, this.name);
|
||||||
|
|
||||||
// Clear the children in our container so we can add an element to click
|
// Clear the children in our container so we can add an element to click
|
||||||
_target.children.clear();
|
_target.children.clear();
|
||||||
@ -131,5 +132,5 @@ class CrossFileTestOverrides {
|
|||||||
Element Function(String href, String suggestedName) createAnchorElement;
|
Element Function(String href, String suggestedName) createAnchorElement;
|
||||||
|
|
||||||
/// Default constructor for overrides
|
/// Default constructor for overrides
|
||||||
CrossFileTestOverrides({required this.createAnchorElement});
|
CrossFileTestOverrides({this.createAnchorElement});
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,12 @@ class XFile extends XFileBase {
|
|||||||
/// (like in web)
|
/// (like in web)
|
||||||
XFile(
|
XFile(
|
||||||
String path, {
|
String path, {
|
||||||
String? mimeType,
|
String mimeType,
|
||||||
String? name,
|
String name,
|
||||||
int? length,
|
int length,
|
||||||
Uint8List? bytes,
|
Uint8List bytes,
|
||||||
DateTime? lastModified,
|
DateTime lastModified,
|
||||||
@visibleForTesting CrossFileTestOverrides? overrides,
|
@visibleForTesting CrossFileTestOverrides overrides,
|
||||||
}) : super(path) {
|
}) : super(path) {
|
||||||
throw UnimplementedError(
|
throw UnimplementedError(
|
||||||
'CrossFile is not available in your current platform.');
|
'CrossFile is not available in your current platform.');
|
||||||
@ -35,12 +35,12 @@ class XFile extends XFileBase {
|
|||||||
/// Construct a CrossFile object from its data
|
/// Construct a CrossFile object from its data
|
||||||
XFile.fromData(
|
XFile.fromData(
|
||||||
Uint8List bytes, {
|
Uint8List bytes, {
|
||||||
String? mimeType,
|
String mimeType,
|
||||||
String? name,
|
String name,
|
||||||
int? length,
|
int length,
|
||||||
DateTime? lastModified,
|
DateTime lastModified,
|
||||||
String? path,
|
String path,
|
||||||
@visibleForTesting CrossFileTestOverrides? overrides,
|
@visibleForTesting CrossFileTestOverrides overrides,
|
||||||
}) : super(path) {
|
}) : super(path) {
|
||||||
throw UnimplementedError(
|
throw UnimplementedError(
|
||||||
'CrossFile is not available in your current platform.');
|
'CrossFile is not available in your current platform.');
|
||||||
@ -54,5 +54,5 @@ class CrossFileTestOverrides {
|
|||||||
dynamic Function(String href, String suggestedName) createAnchorElement;
|
dynamic Function(String href, String suggestedName) createAnchorElement;
|
||||||
|
|
||||||
/// Default constructor for overrides
|
/// Default constructor for overrides
|
||||||
CrossFileTestOverrides({required this.createAnchorElement});
|
CrossFileTestOverrides({this.createAnchorElement});
|
||||||
}
|
}
|
||||||
|
@ -11,20 +11,20 @@ import './base.dart';
|
|||||||
/// A CrossFile backed by a dart:io File.
|
/// A CrossFile backed by a dart:io File.
|
||||||
class XFile extends XFileBase {
|
class XFile extends XFileBase {
|
||||||
final File _file;
|
final File _file;
|
||||||
final String? mimeType;
|
final String mimeType;
|
||||||
final DateTime? _lastModified;
|
final DateTime _lastModified;
|
||||||
int? _length;
|
int _length;
|
||||||
|
|
||||||
final Uint8List? _bytes;
|
final Uint8List _bytes;
|
||||||
|
|
||||||
/// Construct a CrossFile object backed by a dart:io File.
|
/// Construct a CrossFile object backed by a dart:io File.
|
||||||
XFile(
|
XFile(
|
||||||
String path, {
|
String path, {
|
||||||
this.mimeType,
|
this.mimeType,
|
||||||
String? name,
|
String name,
|
||||||
int? length,
|
int length,
|
||||||
Uint8List? bytes,
|
Uint8List bytes,
|
||||||
DateTime? lastModified,
|
DateTime lastModified,
|
||||||
}) : _file = File(path),
|
}) : _file = File(path),
|
||||||
_bytes = null,
|
_bytes = null,
|
||||||
_lastModified = lastModified,
|
_lastModified = lastModified,
|
||||||
@ -34,10 +34,10 @@ class XFile extends XFileBase {
|
|||||||
XFile.fromData(
|
XFile.fromData(
|
||||||
Uint8List bytes, {
|
Uint8List bytes, {
|
||||||
this.mimeType,
|
this.mimeType,
|
||||||
String? path,
|
String path,
|
||||||
String? name,
|
String name,
|
||||||
int? length,
|
int length,
|
||||||
DateTime? lastModified,
|
DateTime lastModified,
|
||||||
}) : _bytes = bytes,
|
}) : _bytes = bytes,
|
||||||
_file = File(path ?? ''),
|
_file = File(path ?? ''),
|
||||||
_length = length,
|
_length = length,
|
||||||
@ -84,7 +84,7 @@ class XFile extends XFileBase {
|
|||||||
@override
|
@override
|
||||||
Future<String> readAsString({Encoding encoding = utf8}) {
|
Future<String> readAsString({Encoding encoding = utf8}) {
|
||||||
if (_bytes != null) {
|
if (_bytes != null) {
|
||||||
return Future.value(String.fromCharCodes(_bytes!));
|
return Future.value(String.fromCharCodes(_bytes));
|
||||||
}
|
}
|
||||||
return _file.readAsString(encoding: encoding);
|
return _file.readAsString(encoding: encoding);
|
||||||
}
|
}
|
||||||
@ -97,13 +97,13 @@ class XFile extends XFileBase {
|
|||||||
return _file.readAsBytes();
|
return _file.readAsBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Uint8List> _getBytes(int? start, int? end) async* {
|
Stream<Uint8List> _getBytes(int start, int end) async* {
|
||||||
final bytes = _bytes!;
|
final bytes = _bytes;
|
||||||
yield bytes.sublist(start ?? 0, end ?? bytes.length);
|
yield bytes.sublist(start ?? 0, end ?? bytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<Uint8List> openRead([int? start, int? end]) {
|
Stream<Uint8List> openRead([int start, int end]) {
|
||||||
if (_bytes != null) {
|
if (_bytes != null) {
|
||||||
return _getBytes(start, end);
|
return _getBytes(start, end);
|
||||||
} else {
|
} else {
|
||||||
|
@ -31,7 +31,7 @@ Element ensureInitialized(String id) {
|
|||||||
if (target == null) {
|
if (target == null) {
|
||||||
final Element targetElement = Element.tag('flt-x-file')..id = id;
|
final Element targetElement = Element.tag('flt-x-file')..id = id;
|
||||||
|
|
||||||
querySelector('body')!.children.add(targetElement);
|
querySelector('body').children.add(targetElement);
|
||||||
target = targetElement;
|
target = targetElement;
|
||||||
}
|
}
|
||||||
return target;
|
return target;
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
name: cross_file
|
name: cross_file
|
||||||
description: An abstraction to allow working with files across multiple platforms.
|
description: An abstraction to allow working with files across multiple platforms.
|
||||||
homepage: https://github.com/flutter/plugins/tree/master/packages/cross_file
|
homepage: https://github.com/flutter/plugins/tree/master/packages/cross_file
|
||||||
version: 0.3.0-nullsafety
|
version: 0.2.1
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
meta: ^1.3.0-nullsafety.3
|
http: ^0.12.0+1
|
||||||
|
meta: ^1.0.5
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
pedantic: ^1.10.0-nullsafety.3
|
pedantic: ^1.8.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.12.0-0 <3.0.0"
|
sdk: ">=2.1.0 <3.0.0"
|
||||||
flutter: ">=1.22.0"
|
flutter: ">=1.22.0"
|
||||||
|
@ -11,8 +11,10 @@ import 'dart:typed_data';
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:cross_file/cross_file.dart';
|
import 'package:cross_file/cross_file.dart';
|
||||||
|
|
||||||
|
import 'dart:html';
|
||||||
|
|
||||||
final String expectedStringContents = 'Hello, world!';
|
final String expectedStringContents = 'Hello, world!';
|
||||||
final Uint8List bytes = Uint8List.fromList(utf8.encode(expectedStringContents));
|
final Uint8List bytes = utf8.encode(expectedStringContents);
|
||||||
final html.File textFile = html.File([bytes], 'hello.txt');
|
final html.File textFile = html.File([bytes], 'hello.txt');
|
||||||
final String textFileUrl = html.Url.createObjectUrl(textFile);
|
final String textFileUrl = html.Url.createObjectUrl(textFile);
|
||||||
|
|
||||||
@ -64,7 +66,7 @@ void main() {
|
|||||||
|
|
||||||
await file.saveTo('');
|
await file.saveTo('');
|
||||||
|
|
||||||
final container = html.querySelector('#${CrossFileDomElementId}');
|
final container = querySelector('#${CrossFileDomElementId}');
|
||||||
|
|
||||||
expect(container, isNotNull);
|
expect(container, isNotNull);
|
||||||
});
|
});
|
||||||
@ -74,18 +76,18 @@ void main() {
|
|||||||
|
|
||||||
await file.saveTo('path');
|
await file.saveTo('path');
|
||||||
|
|
||||||
final container = html.querySelector('#${CrossFileDomElementId}');
|
final container = querySelector('#${CrossFileDomElementId}');
|
||||||
final html.AnchorElement element =
|
final AnchorElement element = container?.children?.firstWhere(
|
||||||
container?.children.firstWhere((element) => element.tagName == 'A')
|
(element) => element.tagName == 'A',
|
||||||
as html.AnchorElement;
|
orElse: () => null);
|
||||||
|
|
||||||
// if element is not found, the `firstWhere` call will throw StateError.
|
expect(element, isNotNull);
|
||||||
expect(element.href, file.path);
|
expect(element.href, file.path);
|
||||||
expect(element.download, file.name);
|
expect(element.download, file.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('anchor element is clicked', () async {
|
test('anchor element is clicked', () async {
|
||||||
final mockAnchor = html.AnchorElement();
|
final mockAnchor = AnchorElement();
|
||||||
|
|
||||||
CrossFileTestOverrides overrides = CrossFileTestOverrides(
|
CrossFileTestOverrides overrides = CrossFileTestOverrides(
|
||||||
createAnchorElement: (_, __) => mockAnchor,
|
createAnchorElement: (_, __) => mockAnchor,
|
||||||
|
@ -15,7 +15,7 @@ final pathPrefix =
|
|||||||
Directory.current.path.endsWith('test') ? './assets/' : './test/assets/';
|
Directory.current.path.endsWith('test') ? './assets/' : './test/assets/';
|
||||||
final path = pathPrefix + 'hello.txt';
|
final path = pathPrefix + 'hello.txt';
|
||||||
final String expectedStringContents = 'Hello, world!';
|
final String expectedStringContents = 'Hello, world!';
|
||||||
final Uint8List bytes = Uint8List.fromList(utf8.encode(expectedStringContents));
|
final Uint8List bytes = utf8.encode(expectedStringContents);
|
||||||
final File textFile = File(path);
|
final File textFile = File(path);
|
||||||
final String textFilePath = textFile.path;
|
final String textFilePath = textFile.path;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user