Line data Source code
1 : import 'dart:async';
2 : import 'dart:convert';
3 : import 'package:http_parser/http_parser.dart';
4 : import 'utils.dart';
5 :
6 : // ignore: uri_does_not_exist
7 : import 'multipart_file_stub.dart'
8 : // ignore: uri_does_not_exist
9 : if (dart.library.io) 'multipart_file_io.dart';
10 :
11 : /// A file to be uploaded as part of a [MultipartRequest]. This doesn't need to
12 : /// correspond to a physical file.
13 : ///
14 : /// MultipartFile is based on stream, and a stream can be read only once,
15 : /// so the same MultipartFile can't be read multiple times.
16 : class MultipartFile {
17 : /// The size of the file in bytes. This must be known in advance, even if this
18 : /// file is created from a [ByteStream].
19 : final int length;
20 :
21 : /// The basename of the file. May be null.
22 : final String? filename;
23 :
24 : /// The additional headers the file has. May be null.
25 : final Map<String, List<String>>? headers;
26 :
27 : /// The content-type of the file. Defaults to `application/octet-stream`.
28 : final MediaType? contentType;
29 :
30 : /// The stream that will emit the file's contents.
31 : final Stream<List<int>> _stream;
32 :
33 : /// Whether [finalize] has been called.
34 2 : bool get isFinalized => _isFinalized;
35 : bool _isFinalized = false;
36 :
37 : /// Creates a new [MultipartFile] from a chunked [Stream] of bytes. The length
38 : /// of the file in bytes must be known in advance. If it's not, read the data
39 : /// from the stream and use [MultipartFile.fromBytes] instead.
40 : ///
41 : /// [contentType] currently defaults to `application/octet-stream`, but in the
42 : /// future may be inferred from [filename].
43 1 : MultipartFile(
44 : Stream<List<int>> stream,
45 : this.length, {
46 : this.filename,
47 : MediaType? contentType,
48 : Map<String, List<String>>? headers,
49 : }) : _stream = stream,
50 1 : headers = caseInsensitiveKeyMap(headers),
51 1 : contentType = contentType ?? MediaType('application', 'octet-stream');
52 :
53 : /// Creates a new [MultipartFile] from a byte array.
54 : ///
55 : /// [contentType] currently defaults to `application/octet-stream`, but in the
56 : /// future may be inferred from [filename].
57 1 : factory MultipartFile.fromBytes(
58 : List<int> value, {
59 : String? filename,
60 : MediaType? contentType,
61 : final Map<String, List<String>>? headers,
62 : }) {
63 2 : var stream = Stream.fromIterable([value]);
64 1 : return MultipartFile(
65 : stream,
66 1 : value.length,
67 : filename: filename,
68 : contentType: contentType,
69 : headers: headers,
70 : );
71 : }
72 :
73 : /// Creates a new [MultipartFile] from a string.
74 : ///
75 : /// The encoding to use when translating [value] into bytes is taken from
76 : /// [contentType] if it has a charset set. Otherwise, it defaults to UTF-8.
77 : /// [contentType] currently defaults to `text/plain; charset=utf-8`, but in
78 : /// the future may be inferred from [filename].
79 1 : factory MultipartFile.fromString(
80 : String value, {
81 : String? filename,
82 : MediaType? contentType,
83 : final Map<String, List<String>>? headers,
84 : }) {
85 1 : contentType ??= MediaType('text', 'plain');
86 3 : var encoding = encodingForCharset(contentType.parameters['charset'], utf8);
87 3 : contentType = contentType.change(parameters: {'charset': encoding.name});
88 :
89 1 : return MultipartFile.fromBytes(
90 1 : encoding.encode(value),
91 : filename: filename,
92 : contentType: contentType,
93 : headers: headers,
94 : );
95 : }
96 :
97 : /// Creates a new [MultipartFile] from a path to a file on disk.
98 : ///
99 : /// [filename] defaults to the basename of [filePath]. [contentType] currently
100 : /// defaults to `application/octet-stream`, but in the future may be inferred
101 : /// from [filename].
102 : ///
103 : /// Throws an [UnsupportedError] if `dart:io` isn't supported in this
104 : /// environment.
105 1 : static Future<MultipartFile> fromFile(
106 : String filePath, {
107 : String? filename,
108 : MediaType? contentType,
109 : final Map<String, List<String>>? headers,
110 : }) =>
111 1 : multipartFileFromPath(
112 : filePath,
113 : filename: filename,
114 : contentType: contentType,
115 : headers: headers,
116 : );
117 :
118 1 : static MultipartFile fromFileSync(
119 : String filePath, {
120 : String? filename,
121 : MediaType? contentType,
122 : final Map<String, List<String>>? headers,
123 : }) =>
124 1 : multipartFileFromPathSync(
125 : filePath,
126 : filename: filename,
127 : contentType: contentType,
128 : headers: headers,
129 : );
130 :
131 1 : Stream<List<int>> finalize() {
132 1 : if (isFinalized) {
133 0 : throw StateError("Can't finalize a finalized MultipartFile.");
134 : }
135 1 : _isFinalized = true;
136 1 : return _stream;
137 : }
138 : }
|