I'm using Flutter web and strapi headless cms for backend. I'm able to send the files successfully, but would like its progress indication. Backend restrictions: File upload must be multipart form-data, being it a buffer or stream. Frontend restrictions: Flutter web doesn't have access to system file directories; files must be loaded in memory and sent using its bytes.
I'm able to upload the file using flutter's http package or the Dio package, but have the following problems when trying to somehow access upload progress:
Http example code:
http.StreamedResponse response;
final uri = Uri.parse(url);
final request = MultipartRequest(
'POST',
uri,
);
request.headers['authorization'] = 'Bearer $_token';
request.files.add(http.MultipartFile.fromBytes(
'files',
_fileToUpload.bytes,
filename: _fileToUpload.name,
));
response = await request.send();
var resStream = await response.stream.bytesToString();
var resData = json.decode(resStream);
What I tryed:
When acessing the response.stream for the onData, it only responds when the server sends the finished request (even though the methods states it's supposed to gets some indications of progress).
Dio package code
Response response = await dio.post(url,
data: formData,
options: Options(
headers: {
'authorization': 'Bearer $_token',
},
), onSendProgress: (int sent, int total) {
setState(() {
pm.progress = (sent / total) * 100;
});
The problems:
It seems the package is able to get some progress indication, but Dio package for flutter web has a bug which has not been fixed: requests block the ui and the app freezes until upload is finished.
Hi you can use the universal_html/html.dart package to do the progress bar, here are steps:
to import universal package
import 'package:universal_html/html.dart' as html;
Select files from html input element instead using file picker packages
_selectFile() {
html.FileUploadInputElement uploadInput = html.FileUploadInputElement();
uploadInput.multiple = false;
uploadInput.accept = '.png,.jpg,.glb';
uploadInput.click();
uploadInput.onChange.listen((e) {
_file = uploadInput.files.first;
});
}
Create upload_worker.js into web folder, my example is upload into S3 post presigned url
self.addEventListener('message', async (event) => {
var file = event.data.file;
var url = event.data.uri;
var postData = event.data.postData;
uploadFile(file, url, postData);
});
function uploadFile(file, url, presignedPostData) {
var xhr = new XMLHttpRequest();
var formData = new FormData();
// if you use postdata, you can open the comment
//Object.keys(presignedPostData).forEach((key) => {
// formData.append(key, presignedPostData[key]);
//});
formData.append('Content-Type', file.type);
// var uploadPercent;
formData.append('file', file);
xhr.upload.addEventListener("progress", function (e) {
if (e.lengthComputable) {
console.log(e.loaded + "/" + e.total);
// pass progress bar status to flutter widget
postMessage(e.loaded/e.total);
}
});
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
// postMessage("done");
}
}
xhr.onerror = function () {
console.log('Request failed');
// only triggers if the request couldn't be made at all
// postMessage("Request failed");
};
xhr.open('POST', url, true);
xhr.send(formData);
}
Flutter web call upload worker to upload and listener progress bar status
class Upload extends StatefulWidget {
#override
_UploadState createState() => _UploadState();
}
class _UploadState extends State<Upload> {
html.Worker myWorker;
html.File file;
_uploadFile() async {
String _uri = "/upload";
final postData = {};
myWorker.postMessage({"file": file, "uri": _uri, "postData": postData});
}
_selectFile() {
html.InputElement uploadInput = html.FileUploadInputElement();
uploadInput.multiple = false;
uploadInput.click();
uploadInput.onChange.listen((e) {
file = uploadInput.files.first;
});
}
#override
void initState() {
myWorker = new html.Worker('upload_worker.js');
myWorker.onMessage.listen((e) {
setState(() {
//progressbar,...
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
RaisedButton(
onPressed: _selectFile,
child: Text("Select File"),
),
RaisedButton(
onPressed: _uploadFile,
child: Text("Upload"),
),
],
);
}
}
that's it, I hope it can help you.
Related
So, i make a feature to change profile pic. uploading successfully, then i try to get the new pict, but the pict is null because when i print the pict it shows the older pict instead of a new one.
seems like the image network didn't update automatically.
anybody can explain why this happen and how to fix it?
code for uploading
Future upload(File imageFile) async {
var stream = http.ByteStream(imageFile.openRead());
stream.cast();
var length = await imageFile.length();
String uuid = await UserPreference().getUuid();
var uri = Uri.parse("$baseUrl/customer/$uuid");
var request = http.MultipartRequest("POST", uri);
var multipartFile = http.MultipartFile('profile_picture', stream, length,
filename: basename(imageFile.path));
String getToken = await UserPreference().getToken();
request.headers.addAll({
'Content-Type': 'application/json',
HttpHeaders.authorizationHeader: "Bearer $getToken"
});
request.files.add(multipartFile);
var response = await request.send();
print(getToken);
if (response.statusCode == 200) {
print('uploaded');
} else {
print('failed');
}
response.stream.transform(utf8.decoder).listen((value) {
print(value);
});
}
the way i call the pic is
CircleAvatar(
radius: 50,
child: Image.network(
user.profile_picture!,
fit: BoxFit.cover,
),
),
Add setState once after response.
void uploadImage() async {
// Show loader
// open a byteStream
var stream = new
http.ByteStream(DelegatingStream.typed(file.openRead()));
// get file length
var length = await file.length();
Map<String, String> headers = {
"Accept": "application/json",
"Authorization": token
}; // ignore this headers if there is no authentication
// string to uri
var uri = Uri.parse(Constants.BASE_URL);
// create multipart request
var request = new http.MultipartRequest("POST", uri);
// if you need more parameters to parse, add those like this
// to the API request
request.fields["orderId"] = orderID.toString();
// multipart that takes file.. here this "file" is a key of the
// API request
var multipartFile = new http.MultipartFile('file', stream,
length,
filename: basename(file.path));
//add headers
request.headers.addAll(headers);
// add file to multipart
request.files.add(multipartFile);
// send request to upload image
await request.send().then((response) async {
// listen for response
response.stream.transform(utf8.decoder).listen((value) {
print(value);
setState(() {
if (response.statusCode == 200) {
print('uploaded');
} else {
print('failed');
}
});
// Hide loader
});
}).catchError((e) {
print(e);
// Hide loader
});
This will refresh the UI.
I have a web developer background, just a beginner in Flutter and Dart.
For a project, I need to implement Server-Sent-Event / EventSource functionality in Flutter.
I have searched for options like dart:http EventSource constructor and Flutter StreamBuilder but I have not found a working example with API.
Can you give me some examples where the Flutter app can listen to an API?
This is what do in Web development.
// client side
const eventSource = new EventSource(`/eventSource/${xyz}`)
eventSource.onmessage = function(e) {
const data = JSON.parse(e.data)
console.log(e)
}
// server side
router.get('/eventSource/:id', (req, res) => {
const headers = {
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache'
}
res.writeHead(200, headers)
const data = `data: ${JSON.stringify(someData)}\n\n`
setInterval(() => {
x.write(data)
}, 3000)
req.on('close', () => {})
})
This code maybe helpful
var _client;
var _streamResponse;
Future<dynamic> streamFiles() async {
_client = http.Client();
final url = 'url';
var headers = {};
final req = http.Request('GET', Uri.parse(url));
req.headers.addAll(headers);
final res = await _client.send(req);
_streamResponse = res.stream.toStringStream().listen((value) {
print(json.decode(value));
});
}
#override
void dispose() {
if (_streamResponse != null) _streamResponse.cancel();
if (_client != null) _client.close();
super.dispose();
}
Is there a way to upload large files to server?
I am using MultipartRequest with MultipartFile like:
List<int> fileBytes) async {
var request = new http.MultipartRequest("POST", Uri.parse(url));
request.files.add(http.MultipartFile.fromBytes(
'file',
fileBytes,
contentType: MediaType('application', 'octet-stream'),
filename: fileName));
request.headers.addAll(headers);
var streamedResponse = await request.send();
return await http.Response.fromStream(streamedResponse);
and reading the file like:
html.InputElement uploadInput = html.FileUploadInputElement();
uploadInput.multiple = false;
uploadInput.draggable = true;
uploadInput.click();
uploadInput.onChange.listen((e) {
final files = uploadInput.files;
final file = files[0];
final reader = new html.FileReader();
reader.onLoadEnd.listen((e) {
setState(() {
_bytesData =
Base64Decoder().convert(reader.result.toString().split(",").last);
_selectedFile = _bytesData;
});
});
reader.readAsDataUrl(file);
});
It is OK for files around 30 MB but for more than that, I am getting Error code: Out of Memory.
Am I doing something wrong? I saw somewhere
MultipartFile.fromBytes will give you some issues on bigger files, as the browser will limit your memory consumption.
And I think his solution is:
There’s a fromStream constructor. Usually, for bigger files, I just use HttpRequest, and put the File object in a FormData instance.
I used MultipartFile and MultipartFile.fromString and both times (for 150 MB file) that happened again.
How can I use this solution? or Is there a better way to do that for files more than 500 MB?
Update
Added an answer using Worker. This is not a great solution but I think this might help someone.
Currently, I solved the problem using this approach:
Import:
import 'package:universal_html/html.dart' as html;
Flutter part:
class Upload extends StatefulWidget {
#override
_UploadState createState() => _UploadState();
}
class _UploadState extends State<Upload> {
html.Worker myWorker;
html.File file;
_uploadFile() async {
String _uri = "/upload";
myWorker.postMessage({"file": file, "uri": _uri});
}
_selectFile() {
html.InputElement uploadInput = html.FileUploadInputElement();
uploadInput.multiple = false;
uploadInput.click();
uploadInput.onChange.listen((e) {
file = uploadInput.files.first;
});
}
#override
void initState() {
myWorker = new html.Worker('upload_worker.js');
myWorker.onMessage.listen((e) {
setState(() {
//progressbar,...
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
RaisedButton(
onPressed: _selectFile(),
child: Text("Select File"),
),
RaisedButton(
onPressed: _uploadFile(),
child: Text("Upload"),
),
],
);
}
}
Javascript part:
In the web folder (next to index.html), create the file 'upload_worker.js' .
self.addEventListener('message', async (event) => {
var file = event.data.file;
var url = event.data.uri;
uploadFile(file, url);
});
function uploadFile(file, url) {
var xhr = new XMLHttpRequest();
var formdata = new FormData();
var uploadPercent;
formdata.append('file', file);
xhr.upload.addEventListener('progress', function (e) {
//Use this if you want to have a progress bar
if (e.lengthComputable) {
uploadPercent = Math.floor((e.loaded / e.total) * 100);
postMessage(uploadPercent);
}
}, false);
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
postMessage("done");
}
}
xhr.onerror = function () {
// only triggers if the request couldn't be made at all
postMessage("Request failed");
};
xhr.open('POST', url, true);
xhr.send(formdata);
}
I solved the problem using only Dart code: The way to go is to use a chunk uploader.
This means to manually send the file in little parts. I send 99MB per request for example.
There is already a basic implementation of this online:
https://pub.dev/packages/chunked_uploader
You have to get a stream, this is possible with the file_picker or the drop_zone library. I used the drop_zone library because it provides the file picker and the drop zone functionality. In my code the dynamic file objects come from the drop_zone library.
Maybe you have to adjust the chunk uploader functionality depending one your backend. I use a django backend where I wrote a simple view that saves the files. In case of small files it can receive multipart requests with multiple files, in case of large files it can receive chunks and continiues to write a file if a previous chunk was received.
Here some parts of my code:
Python backend:
#api_view(["POST"])
def upload(request):
basePath = config.get("BasePath")
targetFolder = os.path.join(basePath, request.data["taskId"], "input")
if not os.path.exists(targetFolder):
os.makedirs(targetFolder)
for count, file in enumerate(request.FILES.getlist("Your parameter name on server side")):
path = os.path.join(targetFolder, file.name)
print(path)
with open(path, 'ab') as destination:
for chunk in file.chunks():
destination.write(chunk)
return HttpResponse("File(s) uploaded!")
flutter chunk uploader in my version:
import 'dart:async';
import 'dart:html';
import 'dart:math';
import 'package:dio/dio.dart';
import 'package:flutter_dropzone/flutter_dropzone.dart';
import 'package:http/http.dart' as http;
class UploadRequest {
final Dio dio;
final String url;
final String method;
final String fileKey;
final Map<String, String>? bodyData;
final Map<String, String>? headers;
final CancelToken? cancelToken;
final dynamic file;
final Function(double)? onUploadProgress;
late final int _maxChunkSize;
int fileSize;
String fileName;
late DropzoneViewController controller;
UploadRequest(
this.dio, {
required this.url,
this.method = "POST",
this.fileKey = "file",
this.bodyData = const {},
this.cancelToken,
required this.file,
this.onUploadProgress,
int maxChunkSize = 1024 * 1024 * 99,
required this.controller,
required this.fileSize,
required this.fileName,
this.headers
}) {
_maxChunkSize = min(fileSize, maxChunkSize);
}
Future<Response?> upload() async {
Response? finalResponse;
for (int i = 0; i < _chunksCount; i++) {
final start = _getChunkStart(i);
print("start is $start");
final end = _getChunkEnd(i);
final chunkStream = _getChunkStream(start, end);
var request = http.MultipartRequest(
"POST",
Uri.parse(url),
);
//request.headers.addAll(_getHeaders(start, end));
request.headers.addAll(headers!);
//-----add other fields if needed
request.fields.addAll(bodyData!);
request.files.add(http.MultipartFile(
"Your parameter name on server side",
chunkStream,
fileSize,
filename: fileName// + i.toString(),
)
);
//-------Send request
var resp = await request.send();
//------Read response
String result = await resp.stream.bytesToString();
//-------Your response
print(result);
}
return finalResponse;
}
Stream<List<int>> _getChunkStream(int start, int end) async* {
print("reading from $start to $end");
final reader = FileReader();
final blob = file.slice(start, end);
reader.readAsArrayBuffer(blob);
await reader.onLoad.first;
yield reader.result as List<int>;
}
// Updating total upload progress
_updateProgress(int chunkIndex, int chunkCurrent, int chunkTotal) {
int totalUploadedSize = (chunkIndex * _maxChunkSize) + chunkCurrent;
double totalUploadProgress = totalUploadedSize / fileSize;
this.onUploadProgress?.call(totalUploadProgress);
}
// Returning start byte offset of current chunk
int _getChunkStart(int chunkIndex) => chunkIndex * _maxChunkSize;
// Returning end byte offset of current chunk
int _getChunkEnd(int chunkIndex) =>
min((chunkIndex + 1) * _maxChunkSize, fileSize);
// Returning a header map object containing Content-Range
// https://tools.ietf.org/html/rfc7233#section-2
Map<String, String> _getHeaders(int start, int end) {
var header = {'Content-Range': 'bytes $start-${end - 1}/$fileSize'};
if (headers != null) {
header.addAll(headers!);
}
return header;
}
// Returning chunks count based on file size and maximum chunk size
int get _chunksCount {
var result = (fileSize / _maxChunkSize).ceil();
return result;
}
}
Upload code that decides whether to upload multiple files in one request or one file divided to many requests:
//upload the large files
Map<String, String> headers = {
'Authorization': requester.loginToken!
};
fileUploadView.droppedFiles.sort((a, b) => b.size - a.size);
//calculate the sum of teh files:
double sumInMb = 0;
int divideBy = 1000000;
for (UploadableFile file in fileUploadView.droppedFiles) {
sumInMb += file.size / divideBy;
}
var dio = Dio();
int uploadedAlready = 0;
for (UploadableFile file in fileUploadView.droppedFiles) {
if (sumInMb < 99) {
break;
}
var uploadRequest = UploadRequest(
dio,
url: requester.backendApi+ "/upload",
file: file.file,
controller: fileUploadView.controller!,
fileSize: file.size,
fileName: file.name,
headers: headers,
bodyData: {
"taskId": taskId.toString(),
"user": requester.username!,
},
);
await uploadRequest.upload();
uploadedAlready++;
sumInMb -= file.size / divideBy;
}
if (uploadedAlready > 0) {
fileUploadView.droppedFiles.removeRange(0, uploadedAlready);
}
print("large files uploaded");
// upload the small files
//---Create http package multipart request object
var request = http.MultipartRequest(
"POST",
Uri.parse(requester.backendApi+ "/upload"),
);
request.headers.addAll(headers);
//-----add other fields if needed
request.fields["taskId"] = taskId.toString();
print("adding files selected with drop zone");
for (UploadableFile file in fileUploadView.droppedFiles) {
Stream<List<int>>? stream = fileUploadView.controller?.getFileStream(file.file);
print("sending " + file.name);
request.files.add(http.MultipartFile(
"Your parameter name on server side",
stream!,
file.size,
filename: file.name));
}
//-------Send request
var resp = await request.send();
//------Read response
String result = await resp.stream.bytesToString();
//-------Your response
print(result);
Hopefully this gives you a good overview how I solved the problem.
I am trying to upload files using dio package in my flutter application. I am sending my files through formdata. Here is my implementation:
Future<FormData> formData1() async {
return FormData.fromMap({
"title": "from app2",
"description": "app upload test",
"files": [
for (var i = 0; i < pathNames.length; i++)
await MultipartFile.fromFile(pathNames[i],
filename: fileNames[i])
]
});
}
Here is how I am sending my files.
_sendToServer() async {
Dio dio = Dio(
BaseOptions(
contentType: 'multipart/form-data',
headers: {
"Authorization": "$token",
},
),
);
dio.interceptors.add(
LogInterceptor(requestBody: true, request: true, responseBody: true));
FormData formData = await formData1();
try {
var response = await dio.post("http://url/api/upload",
data: formData, onSendProgress: (int send, int total) {
print((send / total) * 100);
});
print(response);
} on DioError catch (e) {
if (e.response != null) {
print(e.response.data);
print(e.response.headers);
print(e.response.request);
} else {
print(e.request.headers);
print(e.message);
}
}
}
The other fields in formdata are sent to the server but not the multipartfile. When I try and do the same from postman form-data, it uploads correctly. Am I doing something wrong here?
If you want to upload the file you can convert multipart array before calling API function because even if you put await in form data dio response will not wait for formdata object or you can use MultipartFile.fromFileSync() to get rid of await.
Let me show you in a simple way using my example. try to understand.
Multipart conversion
List multipartArray = [];
for (var i = 0; i < pathNames.length; i++){
multipartArray.add(MultipartFile.fromFileSync(pathNames[i], filename:
basename(pathNames[i])));
}
Api side
static Future<Response> createPostApi(multipartArray) async {
var uri = Uri.parse('http://your_base_url/post');
return await Dio()
.post('$uri',
data: FormData.fromMap({
"title": "from app2",
"description": "app upload test",
"files": multipartArray
}))
.catchError((e) {
print(e.response.data);
print(e.response.headers);
print(e.response.request);
});
}
Here is my code where I used file_picker flutter library and MediaType('application', 'pdf') to ensure that the content passed to the API was indeed a .pdf file.
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:http_parser/http_parser.dart';
static Future<dynamic> uploadfile(int userid, File file, String token) async {
var fileName = file.path.split('/').last;
print(fileName);
var formData = FormData.fromMap({
'title': 'Upload Dokumen',
'uploaded_file': await MultipartFile.fromFile(file.path,
filename: fileName, contentType: MediaType('application', 'pdf')),
"type": "application/pdf"
});
var response = await Dio().post('${urlapi}request/',
options: Options(
contentType: 'multipart/form-data',
headers: {HttpHeaders.authorizationHeader: 'Token $token'}),
data: formData);
print(response);
return response;
}
The file picker:
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null) {
File file = File(result.files.single.path ??'file.pdf');
BlocProvider.of<UploadCubit>(context)
.uploadFile(statelogin.user.id, file,
statelogin.user.token);
}
Change formdata with following rest is fine
import 'package:path/path.dart' as pathManager;
import 'package:mime/mime.dart' as mimeManager;
FormData formdata = FormData();
formdata.add(
"files",
[UploadFileInfo(img, pathManager.basename(img.path),
contentType:
ContentType.parse(mimeManager.lookupMimeType(img.path)))]);
// here attachmentFile is File instance, which is set by File Picker
Map<String, dynamic> _documentFormData = {};
if (attachmentFile != null) {
_documentFormData['document_file'] = MultipartFile.fromFileSync(attachmentFile.path);
}
FormData formData = FormData.fromMap(_documentFormData);
try {
var response = await dio.post("http://url/api/upload",
data: formData, onSendProgress: (int send, int total) {
print((send / total) * 100);
});
print(response);
} on DioError catch (e) {
if (e.response != null) {
print(e.response.data);
print(e.response.headers);
print(e.response.request);
} else {
print(e.request.headers);
print(e.message);
}
}
Here you can use MultipartRequest class without using any of library to upload any kind of files using restAPI.
void uploadFile(File file) async {
// string to uri
var uri = Uri.parse("enter here upload URL");
// create multipart request
var request = new http.MultipartRequest("POST", uri);
// if you need more parameters to parse, add those like this. i added "user_id". here this "user_id" is a key of the API request
request.fields["user_id"] = "text";
// multipart that takes file.. here this "idDocumentOne_1" is a key of the API request
MultipartFile multipartFile = await http.MultipartFile.fromPath(
'idDocumentOne_1',
file.path
);
// add file to multipart
request.files.add(multipartFile);
// send request to upload file
await request.send().then((response) async {
// listen for response
response.stream.transform(utf8.decoder).listen((value) {
print(value);
});
}).catchError((e) {
print(e);
});
}
I used file picker to pick file. Here is the codes for pick file.
Future getPdfAndUpload(int position) async {
File file = await FilePicker.getFile(
type: FileType.custom,
allowedExtensions: ['pdf','docx'],
);
if(file != null) {
setState(() {
file1 = file; //file1 is a global variable which i created
});
}
}
here file_picker flutter library.
This question already has an answer here:
Upload image with http.post and registration form in Flutter?
(1 answer)
Closed 3 years ago.
I am new to Flutter development. My problem is that I try to upload the image but I keep getting failed request.
This piece of code is where I connect it with a server API which will receive the image file from Flutter. String attachment which consist of the image path that is passed from createIncident function located at another page.
Future<IncidentCreateResponse> createIncident( String requesterName, String requesterEmail,
String requesterMobile, String attachment, String title,
String tags, String body, String teamId,
String address ) async {
IncidentCreateResponse incidentCreateResponse;
var url = GlobalConfig.API_BASE_HANDESK + GlobalConfig.API_INCIDENT_CREATE_TICKETS;
var token = Auth().loginSession.accessToken;
var postBody = new Map<String, dynamic>();
postBody["requester_name"] = requesterName;
postBody["requester_email"] = requesterEmail;
postBody["requester_mobile_no"] = requesterMobile;
postBody["attachment"] = attachment;
postBody["title"] = title;
postBody["tags"] = tags;
postBody["body"] = body;
postBody["teamId"] = teamId;
postBody["address"] = address;
// Await the http get response, then decode the json-formatted responce.
var response = await http.post(
url,
body: postBody,
headers: {
'X-APP-ID': GlobalConfig.APP_ID,
"Accept": "application/json; charset=UTF-8",
// "Content-Type": "application/x-www-form-urlencoded",
HttpHeaders.authorizationHeader: 'Bearer $token',
'token': GlobalConfig.API_INCIDENT_REPORT_TOKEN
}
);
if ((response.statusCode == 200) || (response.statusCode == 201)) {
print(response.body);
var data = json.decode(response.body);
incidentCreateResponse = IncidentCreateResponse.fromJson(data['data']);
} else {
print("createIncident failed with status: ${response.statusCode}.");
incidentCreateResponse = null;
}
return incidentCreateResponse;
}
This is the code snippet where I get the image path from the selected image from the gallery
Future getImageFromGallery(BuildContext context) async {
var picture = await ImagePicker.pickImage(source: ImageSource.gallery);
setState((){
_imageFile = picture;
attachment = basename(_imageFile.path);
});
Navigator.of(context).pop();
}
This is the code where I passed the attachment string to the HTTP Response
this.incidentService.createIncident(
Auth().loginSession.name,
Auth().loginSession.email,
Auth().loginSession.mobile_no,
this.attachment,
this._titleController.text,
this._tags,
this._contentController.text,
this._teamId,
this._addressController.text
).then((IncidentCreateResponse res) {
if (res != null) {
print('Ticket Id: ' + res.id);
// Navigator.pop(context);
this._successSubmittionDialog(context);
} else {
this._errorSubmittionDialog(context);
}
}
You can upload image using multipart or base64 Encode.
For uploading image using multipart Visit the Official documentation
For uploading image using base64 Encode you can checkout the Tutorial Here
I suggest using multipart image upload as it is even reliable when your image or files are larger in size.
Hope this could help you,
create a function to upload your image after picking or clicking an image like,
Future<ResponseModel> uploadPhoto(
String _token,
File _image,
String _path,
) async {
Dio dio = new Dio();
FormData _formdata = new FormData();
_formdata.add("photo", new UploadFileInfo(_image, _path));
final response = await dio.post(
baseUrl + '/image/upload',
data: _formdata,
options: Options(
method: 'POST',
headers: {
authTokenHeader: _token,
},
responseType: ResponseType.json,
),
);
if (response.statusCode == 200 || response.statusCode == 500) {
return ResponseModel.fromJson(json.decode(response.toString()));
} else {
throw Exception('Failed to upload!');
}
}
then you can use use uploadImage,
uploadImage(_token, _image,_image.uri.toFilePath()).then((ResponseModel response) {
//do something with the response
});
I have used Dio for the task, you can find more detail about dio here
Add this to your package's pubspec.yaml file:
dependencies:
dio: ^3.0.5
Then import it in your Dart code, you can use:
import 'package:dio/dio.dart';
To upload image using multipart API use this code ie
Add this library dio in your project in pubspec.yaml file
dio: ^3.0.5
and import this in your class
import 'package:dio/dio.dart';
Declare this variable in your class like State<CustomClass>
static var uri = "BASE_URL_HERE";
static BaseOptions options = BaseOptions(
baseUrl: uri,
responseType: ResponseType.plain,
connectTimeout: 30000,
receiveTimeout: 30000,
validateStatus: (code) {
if (code >= 200) {
return true;
}
});
static Dio dio = Dio(options);
then use this method to upload file
Future<dynamic> _uploadFile() async {
try {
Options options = Options(
//contentType: ContentType.parse('application/json'), // only for json type api
);
var directory = await getExternalStorageDirectory(); // directory path
final path = await directory.path; // path of the directory
Response response = await dio.post('/update_profile',
data: FormData.from({
"param_key": "value",
"param2_key": "value",
"param3_key": "value",
"profile_pic_param_key": UploadFileInfo(File("$path/pic.jpg"), "pic.jpg"),
}),
options: options);
setState(() {
isLoading = false;
});
if (response.statusCode == 200 || response.statusCode == 201) {
var responseJson = json.decode(response.data);
return responseJson;
} else if (response.statusCode == 401) {
print(' response code 401');
throw Exception("Incorrect Email/Password");
} else
throw Exception('Authentication Error');
} on DioError catch (exception) {
if (exception == null ||
exception.toString().contains('SocketException')) {
throw Exception("Network Error");
} else if (exception.type == DioErrorType.RECEIVE_TIMEOUT ||
exception.type == DioErrorType.CONNECT_TIMEOUT) {
throw Exception(
"Could'nt connect, please ensure you have a stable network.");
} else {
return null;
}
}
}