Flutter:Dart unzip & save response from http - flutter

i need help for this:
Using Flutter 1.12.13+hotfix.5 & Dart 2.7.0
i receive a zip response.bodyBytes from an http.get
how can i zipdecode and save directly to disk ?
This an extract of my code to show what i'm searching:
import 'dart:async';
import 'dart:io';
...
var basicAuth = 'my auth';
Map<String, String> headers = new HashMap();
headers.putIfAbsent('authorization', () => basicAuth);
headers.putIfAbsent('Content-Type', () => 'application/json');
var url = 'http://myhost';
http.Response response = await http.get(
url,
headers: headers,
);
var dir = await getApplicationDocumentsDirectory();
String path = dir.path + '/';
String fn = 'filename.xxx';
new File(path + fn).writeAsBytes(response.bodyBytes); // This cmd store zip file to disk
// In my case i've a zip file with only one file inside, below a code to unzip all files inside zip
// Extract the contents of the Zip archive to disk.
for (ArchiveFile file in archive) {
String filename = file.name;
if (file.isFile) {
List<int> data = file.content;
File('out/' + filename)
..createSync(recursive: true)
..writeAsBytesSync(data);
} else {
Directory('out/' + filename)
..create(recursive: true);
}
}
// Instead i'd like to unzip the only one file there's inside

IF your data is in gzip format you can try
GZipCodec().decode(response.bodyBytes);

Related

how do i read xml file value

this is my XML data file:
<?xml version = "1.0"?>
<contact-info>
<name>Tanmay Patil</name>
<company>TutorialsPoint</company>
<phone>(011) 123-4567</phone>
</contact-info>
and this is my flutter code:
Future _xml() async {
http.Response response = await http.get(Uri.parse(url));
XmlData = XmlDocument.parse(response.body);
final wantData = XmlData!.findAllElements('contact-info');
}
please how i can get the value TutorialsPoint from my xml file
Use the xml package
import 'package:xml/xml.dart' as xml;
var xmldata='<?xml version = "1.0"?>
<contact-info>
<name>Tanmay Patil</name>
<company>TutorialsPoint</company>
<phone>(011) 123-4567</phone>
</contact-info>';
void main() {
var contact = xml.parse(xmldata);
print(contact.findAllElements('phone'));
}

Uploading files to sharepoint with RestSharp and their rest API is adding header and trailer lines to every file

I'm uploading to sharepoint using a c# client, and every file I Upload gets extra data included. A one line CSV file gets turned into a 7 line file that isn't usable. This is what my one line file upload turned into:
-----------AEE7A299-297A-41E0-B1CC-A72050FCDD28
Content-Disposition: form-data; name="ControlFile_RCTI_statement_20220218_145832.csv"; filename="ControlFile_RCTI_statement_20220218_145832.csv"
Content-Type: application/octet-stream
File;Class;Account Number;Effective Date;Product;Account Type;Document Name
-----------AEE7A299-297A-41E0-B1CC-A72050FCDD28--
My upload code is using restSharp
public async Task UploadFile(string filePath, string list, string folderPath)
{
await CheckTokenAsync();
var fileName = Path.GetFileName(filePath);
var endpoint = $"{spCredentials.sharepointSubSiteFullUrl}/_api/web/GetFolderByServerRelativeUrl('{list}/{folderPath}')/Files/Add(url='{fileName}', overwrite=false)";
var client = new RestClient(endpoint);
client.Timeout = 30000;
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", $"Bearer {token}");
request.AddHeader("Accept", "application/json;odata=verbose");
request.AddFile(fileName, filePath);
var response = await client.ExecuteAsync(request);
if (response.StatusCode == HttpStatusCode.OK)
{
var fileData = JsonConvert.DeserializeObject<SPSingleResultContainer>(response.Content);
var link = fileData.d.__metadata.uri;
await SendRequest<string>($"{link}/CheckIn()", Method.POST);
}
else
throw new Exception($"Upload Failed with message: " + response.ErrorMessage);
}
I've also added this question to the sharepoint SE at https://sharepoint.stackexchange.com/questions/300550/uploading-files-to-sharepoint-with-restsharp-and-their-rest-api-is-adding-header
Turned out RestSharp was doing a multipart upload, and sharepoint doesn't like that sort of thing. Other people have had This Issue with RestSharp
public async Task UploadFile(string filePath, string list, string folderPath)
{
var bytes = File.ReadAllBytes(filePath);
await UploadFileData(Path.GetFileName(filePath), list, folderPath, bytes);
return;
}
public async Task UploadFileData(string fileName, string list, string folderPath, byte[] fileData)
{
await CheckTokenAsync();
var endpoint = $"{spCredentials.sharepointSubSiteFullUrl}/_api/web/GetFolderByServerRelativeUrl('{list}/{folderPath}')/Files/Add(url='{fileName}', overwrite=false)";
var client = new RestClient(endpoint);
client.Timeout = 30000;
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", $"Bearer {token}");
request.AddHeader("Accept", "application/json;odata=verbose");
string contentType = "";
var fileType = Path.GetExtension(fileName).ToLower();
switch (fileType)//there are better ways to get the MIME type, I was just getting desperate and trying everything
{
case ".csv":
contentType = "text/csv";
break;
case ".xlsx":
contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
break;
case ".pdf":
contentType = "application/pdf";
break;
case ".html":
contentType = "text/html";
break;
default:
throw new NotImplementedException($"File type {fileType} not supported");
}
request.AddHeader("Content-Type", contentType);
request.AddParameter(contentType, fileData, ParameterType.RequestBody);
var response = await client.ExecuteAsync(request);
var test = JsonConvert.SerializeObject(request);
if (response.StatusCode == HttpStatusCode.OK)
{
var fileMetaData = JsonConvert.DeserializeObject<SPSingleResultContainer>(response.Content);
var link = fileMetaData.d.__metadata.uri;
await SendRequest<string>($"{link}/CheckIn()", Method.POST);
}
else
throw new Exception($"Upload {fileName} Failed with status {response.StatusCode} and message: " + response.ErrorMessage);
}
For anyone coming here that doesn't care about sharepoint, replacing .addfile with
request.AddHeader("Content-Type", contentType);
request.AddParameter(contentType, fileData, ParameterType.RequestBody);
where contentType is the MIME format of your file extension (or an empty string seems to work as well) solved the issue for me.

How to upload large files in flutter web?

There is already a great thread about how to pick files:
Article
I end up with List<PlatformFile>? files if I use file_picker.
I passed withReadStream: true to the pickFiles method so I get a stream.
Here is my code so far:
List<PlatformFile>? files = fileUploadView.result?.files;
for (PlatformFile file in files!) {
//-----add selected file with request
request.files.add(http.MultipartFile(
"Your parameter name on server side", file.readStream!,
file.size,
filename: file.name));
}
//-------Send request
var resp = await request.send();
But if I run it an error occours every few seconds:
RangeError: Array buffer allocation failed
at new ArrayBuffer (<anonymous>)
at new Uint8Array (<anonymous>)
at Function._create1 (http://localhost:54372/dart_sdk.js:32192:14)
at Function.new (http://localhost:54372/dart_sdk.js:32155:49)
A few weeks later I am able to post an answer: 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.

Download a sub stream with youtube_explode_dart

I'm developing a Flutter app, using the package youtube_explode_dart.
Currently I download all the audio stream, then crop it using ffmpeg. Quickly this looks like :
var id = "aboZctrHfK8";
var file = File("mySong.mp4");
var start = "2000ms";
var end = "5000ms";
// Downloading audio only
var manifest = await yt.videos.streamsClient.getManifest(id);
var audio = manifest.audioOnly.first;
var audioStream = yt.videos.streamsClient.get(audio);
var output = file.openWrite(mode: FileMode.writeOnlyAppend);
for (final data in audioStream) {
output.add(data);
}
// Croping audio
FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg();
await _flutterFFmpeg.executeWithArguments([
"-v", "error",
"-ss", start,
"-to", end,
"-i", file.path,
"-acodec", "copy", "myCroppedSong.mp4"]);
Now I'm facing another issue: Some videos are really heavy and take a while to download. This is not acceptable for my end users, especially since I only want part of the original audio.
Is there a way to download only a subset of the audio stream?
Something like:
for (final data in audioStream.subset(start, end)) {
output.add(data);
}
It would be anwsome!
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
// Initialize the YoutubeExplode instance.
final yt = YoutubeExplode();
Future<void> ExplodeDown() async {
stdout.writeln('Type the video id or url: ');
var url = stdin.readLineSync()!.trim();
// Save the video to the download directory.
Directory('downloads').createSync();
// Download the video.
await download(url);
yt.close();
exit(0);
}
Future<void> download(String id) async {
// Get video metadata.
var video = await yt.videos.get(id);
// Get the video manifest.
var manifest = await yt.videos.streamsClient.getManifest(id);
var streams = manifest.videoOnly;
// Get the audio track with the highest bitrate.
var audio = streams.first;
var audioStream = yt.videos.streamsClient.get(audio);
// Compose the file name removing the unallowed characters in windows.
var fileName = '${video.title}.${audio.container.name}'
.replaceAll(r'\', '')
.replaceAll('/', '')
.replaceAll('*', '')
.replaceAll('?', '')
.replaceAll('"', '')
.replaceAll('<', '')
.replaceAll('>', '')
.replaceAll('|', '');
var file = File('downloads/$fileName');
// Delete the file if exists.
if (file.existsSync()) {
file.deleteSync();
}
// Open the file in writeAppend.
var output = file.openWrite(mode: FileMode.writeOnlyAppend);
// Track the file download status.
var len = audio.size.totalBytes;
var count = 0;
// Create the message and set the cursor position.
var msg = 'Downloading ${video.title}.${audio.container.name}';
stdout.writeln(msg);
// Listen for data received.
// var progressBar = ProgressBar();
await for (final data in audioStream) {
// Keep track of the current downloaded data.
count += data.length;
// Calculate the current progress.
var progress = ((count / len) * 100).ceil();
print (progress);
// Update the progressbar.
// progressBar.update(progress);
// Write to file.
output.add(data);
}
await output.close();
}
**How to download load youtube video & audio & play stream audio **
String youTubeLink = "https://www.youtube.com/watch?v=Ja-85lFDSEM";
Future<void> _downloadVideo(youTubeLink) async{
final yt = YoutubeExplode();
final video = await yt.videos.get(youTubeLink);
// Get the video manifest.
final manifest = await yt.videos.streamsClient.getManifest(youTubeLink);
final streams = manifest.muxed;
final audio = streams.first;
final audioStream = yt.videos.streamsClient.get(audio);
final fileName = '${video.title}.${audio.container.name.toString()}'
.replaceAll(r'\', '')
.replaceAll('/', '')
.replaceAll('*', '')
.replaceAll('?', '')
.replaceAll('"', '')
.replaceAll('<', '')
.replaceAll('>', '')
.replaceAll('|', '');
final dir = await getApplicationDocumentsDirectory();
final path = dir.path;
final directory = Directory('$path/video/');
await directory.create(recursive: true);
final file = File('$path/video/$fileName');
final output = file.openWrite(mode: FileMode.writeOnlyAppend);
var len = audio.size.totalBytes;
var count = 0;
var msg = 'Downloading ${video.title}.${audio.container.name}';
stdout.writeln(msg);
await for (final data in audioStream){
count += data.length;
var progress = ((count / len) * 100).ceil();
print(progress);
output.add(data);
}
await output.flush();
await output.close();
}

Flutter/Dart: Saving an image file to "/storage/emulated/0/Picture/AppFolder/"

using
final dir = await getExternalStorageDirectory();
Image get saved in
/storage/emulated/0/Android/data/com.example.flutter_app/files/
But I want to store it in ../0/Pictures/app_name/ so that it shows up in the gallery.
I looked up all over the www and couldn't figure out. Please advise.
You have to first extract the root Path from the returned location
rootPath = /storage/emulated/0/
than create the Pictures and app_name Directory (to avoid exception when the directory doesn't exist)
then save file in /storage/emulated/0/Pictures/app_name/
here's a simple example to help you understand:
...
Directory externalPath = (await getExternalStorageDirectory());
String picturesDirName = "Pictures";
String appNameDirName = "app_name";
// Splitting the externalPath
List<String> externalPathList = externalPath.path.split('/');
// getting Position of 'Android'
int posOfAndroidDir = externalPathList.indexOf('Android');
//Joining the List<Strings> to generate the rootPath with "/" at the end.
String rootPath = externalPathList.sublist(0, posOfAndroidDir).join('/');
rootPath+="/";
//Creating Pictures Directory (if not exist)
Directory picturesDir = Directory(rootPath+picturesDirName+"/");
if (!picturesDir.existsSync()) {
//Creating Directory
await picturesDir.create(recursive: true);
//Directory Created
} else {
//Directory Already Existed
}
//Creating "app_name" Directory (if not exist)
Directory appNameDir = Directory(rootPath+picturesDirName+"/"+appNameDirName+"/");
if (!appNameDir.existsSync()) {
//Creating Directory
await appNameDir.create(recursive: true);
//Directory Created
} else {
//Directory Already Existed
}
//Creating String varible to store the path where you want to save file.
String fileSaveLocation = rootPath+picturesDirName+"/"+appNameDirName+"/";
// Or you can also use templates like this
String fileSaveLocation2 = "$rootPath$picturesDirName/$appNameDirName/";
//Your File Path where you want to save you file.
String filePath = fileSaveLocation+"text.txt";
// Or you can also use templates like this
String filePath2 = "${fileSaveLocation2}test.txt";
...
You can optimize the above code as per your liking.
hope this is the solution you were looking.
Here, Is how you can acheive this,
final Directory extDir = await getExternalStorageDirectory();
String dirPath = '${extDir.path}/app_name/pictures';
dirPath = dirPath.replaceAll("Android/data/com.example.flutter_app/files/", "");
await Directory(dirPath).create(recursive: true);
// start File Operations Here with dirPath variable
Update:
Along with detailed answer below, I came across a couple of plugins which deals specially with media content.
From Flutter.dev docs:
The plugin currently supports access to two file system locations:
gallery_saver Plugin: See Full Example
GallerySaver.saveImage(path).then((bool success) {// code }
Image_gallery_saver: See Full Example
await ImageGallerySaver.saveImage(Uint8List.fromList(response.data), quality: 60, name: "hello");
Creating the path manually: Source
await Permission.storage.request();
if (await Permission.storage.isGranted) {
var dir = await getExternalStorageDirectory();
Dio dio = Dio();
var newPath = "";
List<String> paths = dir.path.split("/");
for (int x = 1; x < paths.length; x++) {
String folder = paths[x];
if (folder != "Android") {
newPath += "/" + folder;
}
else {
break;
}
}
var picfolder = "/Pictures/";
newPath = newPath + picfolder + AppStrings['AppName'];
Other Useful References:
Android Data Storage
MediaStore in Flutter