Как выбрать файлы и изображения для загрузки с веб-флаттера
Я хотел бы знать, как выбрать изображение с компьютера пользователя в мое веб-приложение для загрузки
10 ответов
Принятый ответ устарел, и использование
dart:html
упаковывать непосредственно во Flutter не рекомендуется.
Вместо этого используйте этот пакет: https://pub.dev/packages/file_picker .
Пример использования во Flutter Web:
class FileUploadButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
child: Text('UPLOAD FILE'),
onPressed: () async {
var picked = await FilePicker.platform.pickFiles();
if (picked != null) {
print(picked.files.first.name);
}
},
);
}
}
Обратите внимание, что
FilePickerResult.path
не поддерживается во Flutter Web.
Я попробовал приведенный ниже код, и он сработал.
первый import 'dart:html';
// variable to hold image to be displayed
Uint8List uploadedImage;
//method to load image and update `uploadedImage`
_startFilePicker() async {
InputElement uploadInput = FileUploadInputElement();
uploadInput.click();
uploadInput.onChange.listen((e) {
// read file content as dataURL
final files = uploadInput.files;
if (files.length == 1) {
final file = files[0];
FileReader reader = FileReader();
reader.onLoadEnd.listen((e) {
setState(() {
uploadedImage = reader.result;
});
});
reader.onError.listen((fileEvent) {
setState(() {
option1Text = "Some Error occured while reading the file";
});
});
reader.readAsArrayBuffer(file);
}
});
}
теперь просто любой виджет, вроде кнопки и вызов метода _startFilePicker()
import 'package:http/http.dart' as http;
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
class FileUploadWithHttp extends StatefulWidget {
@override
_FileUploadWithHttpState createState() => _FileUploadWithHttpState();
}
class _FileUploadWithHttpState extends State<FileUploadWithHttp> {
PlatformFile objFile = null;
void chooseFileUsingFilePicker() async {
//-----pick file by file picker,
var result = await FilePicker.platform.pickFiles(
withReadStream:
true, // this will return PlatformFile object with read stream
);
if (result != null) {
setState(() {
objFile = result.files.single;
});
}
}
void uploadSelectedFile() async {
//---Create http package multipart request object
final request = http.MultipartRequest(
"POST",
Uri.parse("Your API URL"),
);
//-----add other fields if needed
request.fields["id"] = "abc";
//-----add selected file with request
request.files.add(new http.MultipartFile(
"Your parameter name on server side", objFile.readStream, objFile.size,
filename: objFile.name));
//-------Send request
var resp = await request.send();
//------Read response
String result = await resp.stream.bytesToString();
//-------Your response
print(result);
}
@override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
//------Button to choose file using file picker plugin
RaisedButton(
child: Text("Choose File"),
onPressed: () => chooseFileUsingFilePicker()),
//------Show file name when file is selected
if (objFile != null) Text("File name : ${objFile.name}"),
//------Show file size when file is selected
if (objFile != null) Text("File size : ${objFile.size} bytes"),
//------Show upload utton when file is selected
RaisedButton(
child: Text("Upload"), onPressed: () => uploadSelectedFile()),
],
),
);
}
}
Я протестировал этот пакет и был очень доволен результатом imagePickerWeb, он возвращает 3 разных типа, он может быть в виде изображения (виджет для предварительного просмотра), байта, файла (загрузка)
тогда вы можете использовать это, чтобы получить значения
html.File _cloudFile;
var _fileBytes;
Image _imageWidget;
Future<void> getMultipleImageInfos() async {
var mediaData = await ImagePickerWeb.getImageInfo;
String mimeType = mime(Path.basename(mediaData.fileName));
html.File mediaFile =
new html.File(mediaData.data, mediaData.fileName, {'type': mimeType});
if (mediaFile != null) {
setState(() {
_cloudFile = mediaFile;
_fileBytes = mediaData.data;
_imageWidget = Image.memory(mediaData.data);
});
}
Загрузка в firebase
не забудьте добавить это в свой index.html
<script src="https://www.gstatic.com/firebasejs/7.5.0/firebase-storage.js"></script>
Загрузка в firebase
import 'package:firebase/firebase.dart' as fb;
uploadToFirebase(File file) async {
final filePath = 'temp/${DateTime.now()}.png';//path to save Storage
try {
fb
.storage()
.refFromURL('urlFromStorage')
.child(filePath)
.put(file);
} catch (e) {
print('error:$e');
}
}
См. Документацию пакета, если у вас все еще есть проблемы
Принятый ответ действительно устарел. Как и предложил jnt , https://pub.dev/packages/file_picker - удобный пакет, когда дело доходит до реализации загрузки изображений с помощью Flutter Web.
Проблема, с которой я столкнулся, - получить представление изображения в формате base64 , поскольку я использовал его для хранения изображений в Firestore . Как мы знаем,
dart:io
не поддерживается Flutter Web и выдает
Unsupported operation: _Namespace
ошибка. Следовательно, используя
File
и чтение байтов файла было невозможным. К счастью, пакет предоставляет API для преобразования загруженного изображения в
Uint8List
. Вот моя реализация:
import 'package:file_picker/file_picker.dart';
...
FilePickerResult? pickedFile;
...
void chooseImage() async {
pickedFile = await FilePicker.platform.pickFiles();
if (pickedFile != null) {
try {
setState(() {
logoBase64 = pickedFile!.files.first.bytes;
});
} catch (err) {
print(err);
}
} else {
print('No Image Selected');
}
}
Если вам нужно сразу отобразить локальное изображение, используйте
Image.memory
.
Image.memory(logoBase64!);
У меня тоже есть эта проблема;
я использую https://pub.dev/packages/file_picker, но в веб-пути flutter не поддерживается;
вы должны использовать байты;
сохраняю байты файла в var _fileBytes и использую в запросе;
var request = http.MultipartRequest('POST', Uri.parse('https://.....com'));
request.headers.addAll(headers);
request.files.add(
http.MultipartFile.fromBytes(
'image',
await ConvertFileToCast(_fileBytes),
filename: fileName,
contentType: MediaType('*', '*')
)
);
request.fields.addAll(fields);
var response = await request.send();
функция ConvertFileToCast:
ConvertFileToCast(data){
List<int> list = data.cast();
return list;
}
это работа для меня :)
Я могу поделиться тем, как яupload image to AWS s3 from flutter web
недавно. Может не совсем соответствовать случаю, который ищет ответ здесь, но я думаю, что это может как-то вдохновить других.
Сначала я пытаюсь использовать пакет amplify_storage_s3 , но пока он не поддерживает Flutter Web. Поэтому вместо этого я использую базовую http-почту.
Пакеты, которые я использую:
- file_picker : для Интернета FileUploadInputElement (из пакета html ) может делать то же самое, но я думаю, что использование этого пакета может упростить задачу.
- dio : Я не уверен, почему я не могу успешно использовать MultipartFile http , поэтому вместо этого я использую это. (возможно, кто-то может предоставить версию с использованием http-пакета)
- мой : перенести расширение в mimetype
Пример кода:
import 'package:flutter/material.dart';
import 'package:dio/dio.dart' as dio;
import 'package:file_picker/file_picker.dart';
import 'package:mime/mime.dart';
class FileUploader extends StatelessWidget {
const FileUploader({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () async {
// 1. Pick an image file
final filePicked = await FilePicker.platform.pickFiles();
if (filePicked != null) {
final file = filePicked.files.single; // PlatformFile
final mimeType = lookupMimeType(file.name) ?? '';
/// 2. Get presigned data somewhere
const url = 'https://s3.amazonaws.com/......';
final fields = {
'bucket': '...',
'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
'X-Amz-Credential': '...',
'X-Amz-Date': '...',
'Policy': '...',
'X-Amz-Signature': '...',
'x-amz-meta-userid': '...',
'Content-Type': mimeType,
'file': dio.MultipartFile.fromBytes(file.bytes ?? []),
};
/// 3. Send file to AWS s3
final formData = dio.FormData.fromMap(fields);
await dio.Dio().post(url, data: formData);
}
},
child: const Icon(Icons.upload),
),
);
}
}
если кому-то интересно, как заставить его работать на мобильных устройствах и в Интернете:
var bytes;
await file!.files.first.readStream!
.map((asciiValue) => bytes = asciiValue)
.toList();
FormData body;
final MultipartFile file = MultipartFile.fromBytes(bytes, filename: "file");
MapEntry<String, MultipartFile> imageEntry = MapEntry("image", file);
body.files.add(imageEntry);
Вы можете загрузить файл во Flutter Web, используя пакет file_picker .
Future<PlatformFile?> uploadFile() async {
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom,
onFileLoading: (status) => print(status),
allowedExtensions: ['pdf', 'jpg', 'png'],
);
if (result != null) {
return result.files.first;
} else {
return null;
}
}
затем вы можете вызвать этот метод, чтобы получить имя и изображение в байтах
var result = await uploadFile();
//result.name => file name
//result.bytes! => file in bytes
Вот мой рабочий код для загрузки с помощью dio. Я использую dio, потому что у него есть функция обратного вызова.
class _FileUploadViewState extends State<FileUploadView> {
@override
void initState() {
super.initState();
}
FilePickerResult? result;
PlatformFile? file;
Response? response;
String? progress;
String? percentage;
Dio dio = Dio();
selectFile() async {
result =
await FilePicker.platform.pickFiles(type: FileType.any, withData: true);
if (result != null) {
file = result?.files.single;
}
//print(file?.name);
//print(file?.bytes?.length);
//print(file?.size);
//print(file?.extension);
//print(file?.path);
setState(() {});
}
Future<void> uploadFile(BuildContext context, User user) async {
final navigator = Navigator.of(context);
final storage = FlutterSecureStorage();
String? token = await storage.read(key: 'jwt');
final formData = FormData.fromMap(
{
'file': MultipartFile.fromBytes(file?.bytes as List<int>,
filename: file?.name)
},
);
dio.options.headers['content-Type'] = 'application/octet-stream';
dio.options.headers["authorization"] = "Bearer $token";
response = await dio.post(
user.fileUrl,
data: formData,
onSendProgress: (int sent, int total) {
percentage = (sent / total * 100).toStringAsFixed(2);
progress = "$sent Bytes of $total Bytes - $percentage % uploaded";
setState(
() {},
);
},
);
if (response!.statusCode == 200) {
....
Мой код для сервера выглядит так:
if err := r.ParseMultipartForm(64 << 20); err != nil {
log.Println("error processing multipart form")
log.Println(err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
file, handler, err := r.FormFile("file")