Skip to content

Commit c17fa69

Browse files
committed
Add back x_file type to file_selector
1 parent 8556d7b commit c17fa69

File tree

12 files changed

+592
-4
lines changed

12 files changed

+592
-4
lines changed

packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
66

77
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
88
import 'package:meta/meta.dart';
9-
import 'package:x_file/x_file.dart';
109

1110
const MethodChannel _channel =
1211
MethodChannel('plugins.flutter.io/file_selector');

packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import 'dart:async';
66

77
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
88
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
9-
import 'package:x_file/x_file.dart';
109

1110
import '../method_channel/method_channel_file_selector.dart';
1211

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
export 'x_file/x_file.dart';
2+
13
export 'x_type_group/x_type_group.dart';
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import 'dart:convert';
2+
import 'dart:typed_data';
3+
4+
/// The interface for a XFile.
5+
///
6+
/// A XFile is a container that wraps the path of a selected
7+
/// file by the user and (in some platforms, like web) the bytes
8+
/// with the contents of the file.
9+
///
10+
/// This class is a very limited subset of dart:io [File], so all
11+
/// the methods should seem familiar.
12+
abstract class XFileBase {
13+
/// Construct a XFile
14+
XFileBase(String path);
15+
16+
/// Save the XFile at the indicated file path.
17+
void saveTo(String path) async {
18+
throw UnimplementedError('saveTo has not been implemented.');
19+
}
20+
21+
/// Get the path of the picked file.
22+
///
23+
/// This should only be used as a backwards-compatibility clutch
24+
/// for mobile apps, or cosmetic reasons only (to show the user
25+
/// the path they've picked).
26+
///
27+
/// Accessing the data contained in the picked file by its path
28+
/// is platform-dependant (and won't work on web), so use the
29+
/// byte getters in the XFile instance instead.
30+
String get path {
31+
throw UnimplementedError('.path has not been implemented.');
32+
}
33+
34+
/// The name of the file as it was selected by the user in their device.
35+
///
36+
/// Use only for cosmetic reasons, do not try to use this as a path.
37+
String get name {
38+
throw UnimplementedError('.name has not been implemented.');
39+
}
40+
41+
/// For web, it may be necessary for a file to know its MIME type.
42+
String get mimeType {
43+
throw UnimplementedError('.mimeType has not been implemented.');
44+
}
45+
46+
/// Get the length of the file. Returns a `Future<int>` that completes with the length in bytes.
47+
Future<int> length() {
48+
throw UnimplementedError('.length() has not been implemented.');
49+
}
50+
51+
/// Synchronously read the entire file contents as a string using the given [Encoding].
52+
///
53+
/// By default, `encoding` is [utf8].
54+
///
55+
/// Throws Exception if the operation fails.
56+
Future<String> readAsString({Encoding encoding = utf8}) {
57+
throw UnimplementedError('readAsString() has not been implemented.');
58+
}
59+
60+
/// Synchronously read the entire file contents as a list of bytes.
61+
///
62+
/// Throws Exception if the operation fails.
63+
Future<Uint8List> readAsBytes() {
64+
throw UnimplementedError('readAsBytes() has not been implemented.');
65+
}
66+
67+
/// Create a new independent [Stream] for the contents of this file.
68+
///
69+
/// If `start` is present, the file will be read from byte-offset `start`. Otherwise from the beginning (index 0).
70+
///
71+
/// If `end` is present, only up to byte-index `end` will be read. Otherwise, until end of file.
72+
///
73+
/// 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.
74+
Stream<Uint8List> openRead([int start, int end]) {
75+
throw UnimplementedError('openRead() has not been implemented.');
76+
}
77+
78+
/// Get the last-modified time for the XFile
79+
Future<DateTime> lastModified() {
80+
throw UnimplementedError('openRead() has not been implemented.');
81+
}
82+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import 'dart:convert';
2+
import 'dart:typed_data';
3+
4+
import 'package:http/http.dart' as http show readBytes;
5+
import 'package:meta/meta.dart';
6+
import 'dart:html';
7+
8+
import '../../web_helpers/web_helpers.dart';
9+
import './base.dart';
10+
11+
/// A XFile that works on web.
12+
///
13+
/// It wraps the bytes of a selected file.
14+
class XFile extends XFileBase {
15+
String path;
16+
17+
final String mimeType;
18+
final Uint8List _data;
19+
final int _length;
20+
final String name;
21+
final DateTime _lastModified;
22+
Element _target;
23+
24+
final XFileTestOverrides _overrides;
25+
26+
bool get _hasTestOverrides => _overrides != null;
27+
28+
/// Construct a XFile object from its ObjectUrl.
29+
///
30+
/// Optionally, this can be initialized with `bytes` and `length`
31+
/// so no http requests are performed to retrieve files later.
32+
///
33+
/// `name` needs to be passed from the outside, since we only have
34+
/// access to it while we create the ObjectUrl.
35+
XFile(
36+
this.path, {
37+
this.mimeType,
38+
this.name,
39+
int length,
40+
Uint8List bytes,
41+
DateTime lastModified,
42+
@visibleForTesting XFileTestOverrides overrides,
43+
}) : _data = bytes,
44+
_length = length,
45+
_overrides = overrides,
46+
_lastModified = lastModified,
47+
super(path);
48+
49+
/// Construct an XFile from its data
50+
XFile.fromData(
51+
Uint8List bytes, {
52+
this.mimeType,
53+
this.name,
54+
int length,
55+
DateTime lastModified,
56+
this.path,
57+
@visibleForTesting XFileTestOverrides overrides,
58+
}) : _data = bytes,
59+
_length = length,
60+
_overrides = overrides,
61+
_lastModified = lastModified,
62+
super(path) {
63+
if (path == null) {
64+
final blob = (mimeType == null) ? Blob([bytes]) : Blob([bytes], mimeType);
65+
this.path = Url.createObjectUrl(blob);
66+
}
67+
}
68+
69+
@override
70+
Future<DateTime> lastModified() async {
71+
if (_lastModified != null) {
72+
return Future.value(_lastModified);
73+
}
74+
return null;
75+
}
76+
77+
Future<Uint8List> get _bytes async {
78+
if (_data != null) {
79+
return Future.value(UnmodifiableUint8ListView(_data));
80+
}
81+
return http.readBytes(path);
82+
}
83+
84+
@override
85+
Future<int> length() async {
86+
return _length ?? (await _bytes).length;
87+
}
88+
89+
@override
90+
Future<String> readAsString({Encoding encoding = utf8}) async {
91+
return encoding.decode(await _bytes);
92+
}
93+
94+
@override
95+
Future<Uint8List> readAsBytes() async {
96+
return Future.value(await _bytes);
97+
}
98+
99+
@override
100+
Stream<Uint8List> openRead([int start, int end]) async* {
101+
final bytes = await _bytes;
102+
yield bytes.sublist(start ?? 0, end ?? bytes.length);
103+
}
104+
105+
/// Saves the data of this XFile at the location indicated by path.
106+
/// For the web implementation, the path variable is ignored.
107+
void saveTo(String path) async {
108+
// Create a DOM container where we can host the anchor.
109+
_target = ensureInitialized('__x_file_dom_element');
110+
111+
// Create an <a> tag with the appropriate download attributes and click it
112+
// May be overridden with XFileTestOverrides
113+
final AnchorElement element =
114+
(_hasTestOverrides && _overrides.createAnchorElement != null)
115+
? _overrides.createAnchorElement(this.path, this.name)
116+
: createAnchorElement(this.path, this.name);
117+
118+
// Clear the children in our container so we can add an element to click
119+
_target.children.clear();
120+
addElementToContainerAndClick(_target, element);
121+
}
122+
}
123+
124+
/// Overrides some functions to allow testing
125+
@visibleForTesting
126+
class XFileTestOverrides {
127+
/// For overriding the creation of the file input element.
128+
Element Function(String href, String suggestedName) createAnchorElement;
129+
130+
/// Default constructor for overrides
131+
XFileTestOverrides({this.createAnchorElement});
132+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import 'dart:typed_data';
2+
import 'package:meta/meta.dart';
3+
4+
import './base.dart';
5+
6+
/// A XFile is a cross-platform, simplified File abstraction.
7+
///
8+
/// It wraps the bytes of a selected file, and its (platform-dependant) path.
9+
class XFile extends XFileBase {
10+
/// Construct a XFile object from its path.
11+
///
12+
/// Optionally, this can be initialized with `bytes` and `length`
13+
/// so no http requests are performed to retrieve data later.
14+
///
15+
/// `name` may be passed from the outside, for those cases where the effective
16+
/// `path` of the file doesn't match what the user sees when selecting it
17+
/// (like in web)
18+
XFile(
19+
String path, {
20+
String mimeType,
21+
String name,
22+
int length,
23+
Uint8List bytes,
24+
DateTime lastModified,
25+
@visibleForTesting XFileTestOverrides overrides,
26+
}) : super(path) {
27+
throw UnimplementedError(
28+
'XFile is not available in your current platform.');
29+
}
30+
31+
/// Construct a XFile object from its data
32+
XFile.fromData(
33+
Uint8List bytes, {
34+
String mimeType,
35+
String name,
36+
int length,
37+
DateTime lastModified,
38+
String path,
39+
@visibleForTesting XFileTestOverrides overrides,
40+
}) : super(path) {
41+
throw UnimplementedError(
42+
'XFile is not available in your current platform.');
43+
}
44+
}
45+
46+
/// Overrides some functions of XFile for testing purposes
47+
@visibleForTesting
48+
class XFileTestOverrides {
49+
/// For overriding the creation of the file input element.
50+
dynamic Function(String href, String suggestedName) createAnchorElement;
51+
52+
/// Default constructor for overrides
53+
XFileTestOverrides({this.createAnchorElement});
54+
}

0 commit comments

Comments
 (0)