I am making flutter web app that should generate a file from user data. And have the option to download the output file.
But I can not find any options/packages which works for flutter web :(
Can someone please help me out?
I am making flutter web app that should generate a file from user data. And have the option to download the output file.
But I can not find any options/packages which works for flutter web :(
Can someone please help me out?
Simple Code for redirecting to download URL
import 'dart:html' as html;
void downloadFile(String url) {
html.AnchorElement anchorElement = new html.AnchorElement(href: url);
anchorElement.download = url;
anchorElement.click();
}
you can use package url_launcher with url_launcher_web
then you can do:
launchUrl(Uri.parse("data:application/octet-stream;base64,${base64Encode(yourFileBytes)}"));
EDIT: you don't need a plugin if you do this
download.dart:
import 'dart:convert';
// ignore: avoid_web_libraries_in_flutter
import 'dart:html';
void download(
List<int> bytes, {
String downloadName,
}) {
// Encode our file in base64
final _base64 = base64Encode(bytes);
// Create the link with the file
final anchor =
AnchorElement(href: 'data:application/octet-stream;base64,$_base64')
..target = 'blank';
// add the name
if (downloadName != null) {
anchor.download = downloadName;
}
// trigger download
document.body.append(anchor);
anchor.click();
anchor.remove();
return;
}
empty_download.dart:
void download(
List<int> bytes, {
String downloadName,
}) {
print('I do nothing');
}
import and use:
import 'empty_download.dart'
if (dart.library.html) 'download.dart';
void main () {
download('I am a test file'.codeUnits, // takes bytes
downloadName: 'test.txt');
}
One way to trigger download is the adapt a common pattern used in "native" javascript, create an anchor element with the download
attribute and trigger a click.
import 'dart:convert';
import 'dart:html';
main() {
File file = // generated somewhere
final rawData = file.readAsBytesSync();
final content = base64Encode(rawData);
final anchor = AnchorElement(
href: "data:application/octet-stream;charset=utf-16le;base64,$content")
..setAttribute("download", "file.txt")
..click();
}
I've found a solution that let me make an authorized request to get a file (with package http.dart) and then download the file using flutter web (with package dart:html). I don't know if someone other could need this, but I wasn't able to find a solution, so here is the code.
import 'package:http/http.dart';
import 'dart:html' as html;
...
var headers = {
'Content-Type': 'application/octet-stream',
'Accept': 'application/octet-stream',
'Authorization' : 'Bearer [TOKEN HERE]'
};
Response res =
await get(url, headers: headers);
if (res.statusCode == 200) {
final blob = html.Blob([res.bodyBytes]);
final url = html.Url.createObjectUrlFromBlob(blob);
final anchor = html.document.createElement('a') as html.AnchorElement
..href = url
..style.display = 'none'
..download = filename;
html.document.body.children.add(anchor);
anchor.click();
html.document.body.children.remove(anchor);
html.Url.revokeObjectUrl(url);
}
A good workaround is to open the hosted file in a new tab using
import 'dart:html' as html;
openInANewTab(url){
html.window.open(url, 'PlaceholderName');
}
Fits well for my use case.
I've used the followig to download file from a URL,
import 'dart:html' as html;
void downloadFileFromDownloadableLink(String url, String fileName) {
html.AnchorElement anchorElement = html.AnchorElement(href: url);
anchorElement.download = fileName;
anchorElement.click();
}
Response to bounty:
the case of an audio file it just starts playing rather than downloading it
I've done same thing with video, but I'm sure it will work with audio as well. I assume, your generated audio is an array or blob
import 'dart:js' as JS;
import 'dart:html' as HTML;
const mimeType = 'audio/wav'; // TODO: Pick a right format
void downloadFile(List chunks) async {
final blob = HTML.Blob(chunks, mimeType);
final objectUrl = await HTML.Url.createObjectUrlFromBlob(blob);
final a = HTML.AnchorElement();
a.href = item.url;
a.download = "my_audio_file_${DateTime.now()}.wav";
HTML.document.body.append(a);
a.click();
// Wait for click event to be processed and cleanup
await Future.delayed(Duration(milliseconds: 100));
a.remove();
HTML.Url.revokeObjectUrl(item.videoObjectUrl);
}
For me, vishwajit76's answer work for me. If you want the file to have a custom name once is downloaded then do the following
`void downloadFile(String url) {
html.AnchorElement anchorElement = html.AnchorElement(href: url);
anchorElement.download = "myDocument.pdf"; //in my case is .pdf
anchorElement.click();
}`
All other solutions are correct but this solutions may disclose your storage path structure. Because downloaded file name includes complete path name of the file. If your path has uid number of firebase users, this may also cause a security breach. This may be an extreme example but it is a good practice to use uid numbers as on file path to create a more secure storage. You can create a temporary download folder as (Download/useremail) on root and copy the file intended to download inside of (Download/useremail) as below node.js code by firebase functions. This function is called when user click the download button from client. You can delete the temporary download folder later.
const functions = require("firebase-functions");
const {Storage} = require("@google-cloud/storage");
const storage = new Storage();
exports.downloadFile = functions.https.onCall(async (data, context)=> {
await storage.
bucket("my-bucket-name").
file(data.filePath).
copy(storage.
bucket("my-bucket-name").
file(`Download/${data.useremail}/${data.fileName}`));
});
You could also use the dio plugin, which supports this sort of thing out of the box. So you could just do:
response = await dio.download('url-to-downloadable', 'path-to-save-to');
And here's their API docs for the same.