I am converting object data to json file and want to use this file to other projects..
where to find this created json file in device...
void main() async {
List<TransactionModel> trans = [
TransactionModel(
date: DateTime.now(),
id: '1',
),
];
var data = trans.map((e) => e.toJson()).toList();
String result = jsonEncode(data);
File backupFile = File('${Directory.systemTemp.path}/logical.json');
await backupFile.writeAsString(result);
print(backupFile.toString());
//print(Directory('/storage/emulated/0').statSync().modeString());
}
Related
I have displayed images and played audio from GET request. Now I need to store the images and .mp3 audio files locally. Is there any way to achieve this. I need to store List of images and audio files. Thank You.
This is the way I get response from API.
"data": [
{
"id": 1052,
"title": "1",
"audio_file": "",
"desc": null,
"display_title": 1,
"audio_src": null,
"image_src": "https://topik.com/storage/uploads/topik/images/20220620092536.jpg"
},
{
"id": 1053,
"title": "2",
"audio_file": "",
"desc": null,
"display_title": 1,
"audio_src": null,
"image_src": "https://topik.com/storage/uploads/topik/images/20220620092545.jpg"
},
]
You can use the path and path_provider to implement this. Sample code for the Api response you shared:
import 'dart:io';
import 'package:http/http.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
/// Returns the response from the api in a Map
Map getResponseMap(String apiUrl) {
var response = http.get(apiUrl);
Map responseMap = jsonDecode(response) as Map<String, dynamic>;
return responseMap;
}
/// Returns the list of files from the api response.
List<Map> getItemsListFromResponseMap(Map responseMap) {
var list = responseMap['data'];
return list.cast<Map>(); // cast the list to a list of Maps.
}
/// Returns the file name with extension.
String getFileNameFromUrl(String url) {
String fileName = url.split('/').toList().last;
return fileName;
}
Future<void> downloadAudioFromAPIResponse(Map singleResponseItem) async {
String audioUrl = singleResponseItem['audio_src'];
await saveNetworkFileToLocalDirectory(audioUrl); // Your file will be saved to the specified directoty in the [saveNetworkFileToLocalDirectory] function below.
}
Future<void> downloadImageFromAPIResponse(Map singleResponseItem) async {
String imgUrl = singleResponseItem['image_src'];
await saveNetworkFileToLocalDirectory(imgUrl); // Your file will be saved to the specified directoty in the [saveNetworkFileToLocalDirectory] function below.
}
Future<void> saveNetworkFileToLocalDirectory(String fileSrcUrl) async {
var response = await http.get(fileSrcUrl);
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String filePath = join(documentDirectory.path, getFileNameFromUrl(fileSrcUrl));
File file = new File(filePath);
await file.writeAsBytes(response.bodyBytes);
// The file has been written at the filePath specified, in this case,
// The app's document directory.
}
You can change the path to which the file is written by changing the getApplicationDocumentsDirectory to something else. For available paths, check out path_provider.
You can use the sample code to download all the files in your response as:
Map mainResponse = getResponseMap("YourApiRequestUrlHere");
// Get list of all item in the response by API.
List<Map> individualItems = getItemsListFromResponseMap(mainResponse);
// pre-evalute the count to improve performance by not having to call .length every time the loop finishes execution.
int count = individualItems.length;
for (int i = 0; i < count; i++) {
Map item = individualItems[i]; // get a single item.
if(item['audio_src'] != null && item['image_src'] != null) {
await downloadAudioFromAPIResponse(item);
await downloadImageFromAPIResponse(item);
} else if (item['audio_src'] != null) {
await downloadAudioFromAPIResponse(item);
} else if (item['image_src'] != null) {
await downloadImageFromAPIResponse(item);
} else {
continue;
}
}
If my answer was helpful, please mark my answer as Correct. Thank you.
After converting json object to dart object
use
Internet_file package
Uint8List bytes = await InternetFile.get(
data.image_src,
headers: headers, /* in case you want to set auth otherwise remove it*/
process: (percentage) {
print('downloadPercentage: $percentage');
},
);
Now use PathProvider
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
print("temp path : " + tempDir.toString());
await writeToFile(bytes, '${tempPath}/filename.png');
print("written");
Below is a snippet of code from a function that uploads a generated QR code (using the qr_flutter package) to firebase storage; then gets the firebase storage url to save in a custom model that is uploaded to firebase firestore (not shown).
This works fine, however I want to upload a file that consists of the QR code bounded by title text above and address text below. (Essentially a Column with children [title, qrFile, address]).
My question is: How do I combine Text and my qrFile into a single image file that I can upload to firebase storage?
String qrString = 'qr_data_here';
final qrValidationResult = QrValidator.validate(
data: qrString,
version: QrVersions.auto,
errorCorrectionLevel: QrErrorCorrectLevel.L,
);
if (qrValidationResult.status == QrValidationStatus.valid) {
final qrCode = qrValidationResult.qrCode;
const String title = 'title_name_here';
final String address = 'address_here';
final painter = QrPainter.withQr(
qr: qrCode!,
color: const Color(0xFF000000),
gapless: true,
embeddedImageStyle: null,
embeddedImage: null,
);
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
final ts = DateTime.now().millisecondsSinceEpoch.toString();
String path = '$tempPath/$ts.png';
// ui is from import 'dart:ui' as ui;
final picData =
await painter.toImageData(2048, format: ui.ImageByteFormat.png);
// writeToFile is seen in code snippet below
await writeToFile(
picData!,
path,
);
} else {
genericErrorDialog(context);
}
// qrStorage is a reference to a folder in firebase storage
await qrStorage.child('name_here').putFile(qrFile);
var url =
await qrStorage.child('name_here').getDownloadURL();
late File qrFile;
Future<void> writeToFile(ByteData data, String path) async {
final buffer = data.buffer;
qrFile = await File(path).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
One solution is to use the screenshot package (https://pub.dev/packages/screenshot). This package has a function to save a widget as an image (without displaying it on screen) as shown below.
ScreenshotController screenshotController = ScreenshotController();
await screenshotController
.captureFromWidget(CustomWidget())
.then((capturedImage) async {
await do_something_with_capturedImage_here();
});
As it relates to my question specifically; Below is the code to generate a qr code, place it in a widget (needs some more formatting) with text, and then save the widget as an image file and upload to firebase.
String qrString = 'qr_data_here';
final qrValidationResult = QrValidator.validate(
data: qrString,
version: QrVersions.auto,
errorCorrectionLevel: QrErrorCorrectLevel.L,
);
if (qrValidationResult.status == QrValidationStatus.valid) {
final qrCode = qrValidationResult.qrCode;
const String title = 'title_name_here';
final String address = 'address_here';
final painter = QrPainter.withQr(
qr: qrCode!,
color: const Color(0xFF000000),
gapless: true,
embeddedImageStyle: null,
embeddedImage: null,
);
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
final ts = DateTime.now().millisecondsSinceEpoch.toString();
String path = '$tempPath/$ts.png';
// ui is from import 'dart:ui' as ui;
final picData =
await painter.toImageData(2048, format: ui.ImageByteFormat.png);
// writeToFile is seen in code snippet below
await writeToFile(
picData!,
path,
);
await screenshotController
.captureFromWidget(Column(
children: [
Text(title),
Image.file(qrFile),
Text(address),
],
))
.then((capturedImage) async {
await widgetToImageFile(capturedImage);
});
} else {
genericErrorDialog(context);
}
// qrStorage is a reference to a folder in firebase storage
await qrStorage.child('name_here').putFile(fullQrFile);
var url =
await qrStorage.child('name_here').getDownloadURL();
ScreenshotController screenshotController = ScreenshotController();
late File qrFile;
late File fullQrFile;
Future<void> writeToFile(ByteData data, String path) async {
final buffer = data.buffer;
qrFile = await File(path).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
Future<void> widgetToImageFile(
Uint8List capturedImage,
) async {
Directory newTempDir = await getTemporaryDirectory();
String newTempPath = newTempDir.path;
final newTs = DateTime.now().millisecondsSinceEpoch.toString();
String path = '$newTempPath/$newTs.png';
fullQrFile = await File(path).writeAsBytes(capturedImage);
}
I'm using just_audio plugin and it has on description a feature: Read from byte stream.
Basically when I put a file (from url) to play, I'm saving the bytes from file so after this step I want to play it locally.
I have a question about how play from byte stream. Can anyone provide an example how to do this? I need to put this on my playlist so it has to be a child of ConcatanatingAudioSource.
The only Audio Source that I found was using it from Uri.
final _playlist = ConcatenatingAudioSource(
children: [
AudioSource.uri(
Uri.parse(
"https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3"),
tag: AudioMetadata(
album: "Science Friday",
title: "ddddd",
artwork:
"https://media.wnyc.org/i/1400/1400/l/80/1/ScienceFriday_WNYCStudios_1400.jpg",
),
)
]
)
This is how I save the bytes:
void getBytes() async {
Uri uri = Uri.parse(url);
var rng = new Random();
// get temporary directory of device.
Directory tempDir = await getTemporaryDirectory();
// get temporary path from temporary directory.
String tempPath = tempDir.path;
// create a new file in temporary path with random file name.
File file = new File('$tempPath' + (rng.nextInt(100)).toString() + '.mp3');
// call http.get method and pass imageUrl into it to get response.
http.Response response = await http.get(uri);
// write bodyBytes received in response to file.
await file.writeAsBytes(response.bodyBytes);
}
Thanks in advance
So it seems that you need to create your own class as an extension of StreamAudioSource.
import 'dart:typed_data';
import 'package:just_audio/just_audio.dart';
class MyJABytesSource extends StreamAudioSource {
final Uint8List _buffer;
MyJABytesSource(this._buffer) : super(tag: 'MyAudioSource');
#override
Future<StreamAudioResponse> request([int? start, int? end]) async {
// Returning the stream audio response with the parameters
return StreamAudioResponse(
sourceLength: _buffer.length,
contentLength: (start ?? 0) - (end ?? _buffer.length),
offset: start ?? 0,
stream: Stream.fromIterable([_buffer.sublist(start ?? 0, end)]),
contentType: 'audio/wav',
);
}
}
And then invoke it like so
await thePlayer.setAudioSource(MyJABytesSource(bytes));
You can call thePlayer.play(). after, but I prefer to use this as a listener.
thePlayer.processingStateStream.listen((ja.ProcessingState state) {
if (state == ja.ProcessingState.ready) {
// I'm using flutter_cache_manager, and it serves all the file
// under the same name, which is fine, but I think this is why
// I need to pause before I can play again.
// (For tracks after the first, the first plays fine.)
// You probably won't need to pause, but I'm not sure.
thePlayer.pause();
thePlayer.play();
} else if (state == ja.ProcessingState.completed) {
// What to do when it completes.
}
});
The nice part about doing this way, is that you don't actually need await keyword, which can be situationally useful. I have it there just to make clear that this is an async function.
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.
How can we choose file from local system drives in Flutter web development . Not in App
Please any one suggest me .
I am using like this
I can able to choose file
But how can i get the file path
You need to get the data and create a MediaInfo class for it
class MediaInfo{
String fileName;
String filePath;
}
Get File Path
Future<MediaInfo> convertFileGetNamePath({#required html.File file}) async {
final Map<String, dynamic> infoData = {};
final reader = html.FileReader();
reader.readAsDataUrl(file);
await reader.onLoad.first;
final fileName = file.name;
infoData.addAll({
'name': fileName,
'path': filePath,
});
MediaInfo webImageInfo = MediaInfo();
webImageInfo.fileName = infoData['name'];
webImageInfo.filePath = infoData['path'];
return webImageInfo;
}
Implement it in your code
MediaInfo getInfo = await convertFileGetNamePath(file: imageFile);
fileName = getInfo.fileName;