Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cross_file] Move from flutter/plugins. #305

Merged
merged 15 commits into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions packages/cross_file/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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 changes: 25 additions & 0 deletions packages/cross_file/LICENSE
Original file line number Diff line number Diff line change
@@ -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 changes: 34 additions & 0 deletions packages/cross_file/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 5 additions & 0 deletions packages/cross_file/lib/cross_file.dart
Original file line number Diff line number Diff line change
@@ -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 changes: 87 additions & 0 deletions packages/cross_file/lib/src/types/base.dart
Original file line number Diff line number Diff line change
@@ -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 changes: 143 additions & 0 deletions packages/cross_file/lib/src/types/html.dart
Original file line number Diff line number Diff line change
@@ -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 changes: 60 additions & 0 deletions packages/cross_file/lib/src/types/interface.dart
Original file line number Diff line number Diff line change
@@ -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;
}
Loading