I have an android app with a webview.
Whenever a user clicks on a button, JavaScript creates a blob, puts text in it and downloads it.
Here's the function that does this:
function saveTextAsFile(A)
{
FillTextToWrite(A);
var textFileAsBlob = new Blob([textToWrite], {
type: 'text/plain'
});
var downloadLink = document.createElement("a");
downloadLink.download = "Analysis.txt";
downloadLink.innerHTML = "Download File";
if (window.webkitURL != null)
{
downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
}
else
{
downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
downloadLink.onclick = destroyClickedElement;
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
}
downloadLink.click();
}
It works fine in any browser, but when I try to download it in the app, nothing happens.
Is there a way to download the blob in the app or is it easier to change the JavaScript?
I need the JavaScript to work on browser as well as on the android app, so sending the blob to the android app in JavaScript will not work on browser.
The solution I found was to detect wheter the javascript is running in Android app or in browser (by adding a string to the user agent).
When in Android app I am sending variable textToWrite (which would normally go into the blob) to the java.
In javascript:
if(App){
Android.sendData(textToWrite);
} else {//make blob}
In java:
myWebView.addJavascriptInterface(new myJavascriptInterface(this), "Android");
public class myJavascriptInterface {
Context mContext;
myJavascriptInterface(Context c) {
mContext = c;
}
#JavascriptInterface
public void sendData(String data) {
//save data as file
}
}
Related
I am building an audio recorder and want to manipulate the audio using Python but am recording the audio using JavaScript. Currently I can download the blob file as a .wav using a link in HTML but simply want to transfer the audio to be read in as a .wav file in Python.
I have thought about saving the file as a .wav locally and then reading it in using Flask but would prefer to not do this. Does anyone know how to do one of these methods? Thanks in advance for any help. Below is my code:
app.js:
URL = window.URL || window.webkitURL;
//to stream audio from getUserMedia() from MediaStream Recording API https://developer.mozilla.org/en-US/docs/Web/API/MediaStream_Recording_API
var gumStream;
//this creates mediarecorder object
var rec;
//this is the media stream audio source node to record
var input;
// shim for AudioContext (shim corrects the existing audio context code) for when it is not available
var AudioContext = window.AudioContext || window.webkitAudioContext;
//variable defining audio context
var audioContext
//variables for the record/pause/stop buttons
var recordButton = document.getElementById("recordButton");
var stopButton = document.getElementById("stopButton");
var pauseButton = document.getElementById("pauseButton");
//add events to buttons upon being clicked
recordButton.addEventListener("click", startRecording);
stopButton.addEventListener("click", stopRecording);
pauseButton.addEventListener("click", pauseRecording);
function startRecording() {
console.log("recordButton clicked");
var constraints = { audio: true, video:false }
//disable record button until success/fail is received from getUserMedia()
recordButton.disabled = true;
stopButton.disabled = false;
pauseButton.disabled = false
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
//success in obtaining stream, so create audio context as sample rate may change after getUserMedia is called (does this on macOS when using AirPods where sr defaults to one set in OS for playback device)
console.log("getUserMedia() success, stream created, initializing Recorder.js ...");
audioContext = new AudioContext();
//update the sr format
document.getElementById("formats").innerHTML="Format: 1 channel pcm # "+audioContext.sampleRate/1000+"kHz"
gumStream = stream;
input = audioContext.createMediaStreamSource(stream);
//create recorded object and configure to record mono sound (1 channel) / no need to record 2 channels as this will double file size and not improve quality of sound
rec = new Recorder(input,{numChannels:1})
//start the recording process
rec.record()
console.log("Recording started");
}).catch(function(err) {
//enable the record button if getUserMedia() fails
recordButton.disabled = false;
stopButton.disabled = true;
pauseButton.disabled = true
});
}
function pauseRecording(){
console.log("pauseButton clicked rec.recording=",rec.recording );
if (rec.recording){
//pause
rec.stop();
pauseButton.innerHTML="Resume";
}else{
//resume
rec.record()
pauseButton.innerHTML="Pause";
}
}
function stopRecording() {
console.log("stopButton clicked");
//disable the stop button, enable the record to allow for new recordings
stopButton.disabled = true;
recordButton.disabled = false;
pauseButton.disabled = true;
//reset pause button in case the recording is stopped while paused
pauseButton.innerHTML="Pause";
//tell the recorder to stop the recording
rec.stop();
//stop microphone access
gumStream.getAudioTracks()[0].stop();
//create the wav blob and pass it on to createDownloadLink
rec.exportWAV(createDownloadLink)*emphasized text*
}
function createDownloadLink(blob) {
var url = URL.createObjectURL(blob);
var au = document.createElement('audio');
var li = document.createElement('li');
var link = document.createElement('a');
//name of .wav file to use during upload and download (without extendion)
var filename = new Date().toISOString();
//add controls to the <audio> element
au.controls = true;
au.src = url;
//save to disk link
link.href = url;
link.download = filename+".wav"; //download forces the browser to donwload the file using the filename
link.innerHTML = "Save to disk";
//add the new audio element to li
li.appendChild(au);
//add the filename to the li
li.appendChild(document.createTextNode(filename+".wav "))
//add the save to disk link to li
li.appendChild(link);
//add the li element to the ol
recordingsList.appendChild(li);
}
main.py:
from flask import request
from flask import render_template
import os
import mysql.connector
app = Flask(__name__)
#app.route("/", methods=['POST', 'GET'])
def index():
if request.method == "POST":
print(type(request.get_data("audio_data")))
if os.path.isfile('./file.wav'):
print("./file.wav exists")
return render_template('index.html', request="POST")
else:
return render_template("index.html")
if __name__ == "__main__":
app.run()```
Safari version < 9.X didn't support download the file.
Code to download the file
function downloadCSV(csv, filename) {
var downloadContainer = angular.element('<div data-tap-disabled="true"><a></a></div>');
// var downloadLink = angular.element('<a></a>'),
var downloadLink = angular.element(downloadContainer.children()[0]),
blob = new Blob([csv], {
type: 'text/csv;charset=utf-8;'
});
if (window.navigator.msSaveOrOpenBlob) {
// download file for IE
navigator.msSaveBlob(blob, filename);
}
else {
downloadLink.attr('href', window.URL.createObjectURL(blob));
downloadLink.attr('download', filename);
downloadLink.attr('style', 'display: none');
angular.element(document).find('body').append(downloadContainer);
// need timeout here since appending DOM takes some time
$timeout(function() {
downloadLink[0].click();
downloadLink.remove();
}, 10);
}
}
Our application to allow the admin to download the file and it works for all browser but not Safari 9.1.2(11601.7.7).Is there any workaround to make in download attribute to make download feature works for Safari?
Thanks
Kim
I can download a PDF using AngularJS in Chrome, but this doesn't appear to work in the latest FireFox, Internet Explorer 11 or Edge (assuming it doesn't work for IE10 either), and I know a shim is needed for IE9. Don't know if this the best shim for this if anyone has an opinion, but currently it doesn't seem to work. I tried it with a response type of blob and arraybuffer just in case that made a difference, and it doesn't.
All this counters what caniuse indicates about using the Blob URLs. Anyone have this working in IE9 and up, and the last couple versions of FF, and can point out what I'm doing wrong?
$http({
url: '/api/v1/download',
method: 'GET',
responseType: 'blob' // or 'arraybuffer'
}).then(function (response) {
// Use the Blob object to create an object URL to download the file
var url = URL.createObjectURL(response.data);
// var url = URL.createObjectURL(new Blob([response], {type: 'application/pdf'})); // arraybuffer version
// Create an anchor to perform download, but don't append to the DOM
anchor.href = downloadUrl;
anchor.download = filename;
anchor.target = '_blank';
anchor.click();
URL.revokeObjectURL(downloadUrl);
anchor = null;
}).catch(function (reason) {
console.log('FAIL', reason);
});
UPDATE
Currently the best (only) answer works for IE10, 11, Edge, FF, and continues to work with Chrome. IE9 won't work using this solution if anyone has another polyfill/shim/other/etc, and Safari doesn't support the download attribute so the solution in the chosen answer doesn't work in an SPA since it just redirects the current page so in both these cases I've just left TODO stubs.
This is an update to the posted answer with more information added in comments for anyone to use or hopefully add to so IE9 and Safari work as expected:
function performDownload(blob, filename) {
// IE9 has no API for handling downloads using Blob objects, and doesn't support the download attribute
if(isIE() == 9) {
// TODO: polyfill/shim/other... change response type to?
}
// Only works for IE10 and up, including Edge
else if (typeof window.navigator.msSaveBlob !== 'undefined') {
// Provides a prompt to save the file to a location of users choice
window.navigator.msSaveBlob(blob, filename);
}
// Browsers that adhere to current standards can implement downloads
// using the Blob object with the download anchor attribute
// ---
// NOTE: Edge 13+ is compliant with both these standards, but Edge 12
// does not support the download anchor attribute so all versions
// have been grouped to use the propriety `msSaveBlob` method
else {
// Use the Blob object to create an object URL to download the file
var URL = window.URL;
var downloadUrl = URL.createObjectURL(blob);
var anchor = document.createElement('a');
if(angular.isDefined(anchor.download)) {
anchor.href = downloadUrl;
anchor.download = filename;
anchor.target = '_blank';
document.body.appendChild(anchor); // Required by Firefox
anchor.click();
// Release the existing object URL, and the anchor
$timeout(function () {
URL.revokeObjectURL(downloadUrl);
document.body.removeChild(anchor);
anchor = null;
}, 100);
}
else {
// TODO: Safari does not support the download anchor attribute...
}
}
}
I've used this with success in both IE11 and Chrome:
function saveBlob(response, contentType, filename) {
let blob = new Blob([response.arrayBuffer()], { type: contentType });
if (typeof window.navigator.msSaveBlob !== 'undefined') {
// IE workaround
window.navigator.msSaveBlob(blob, filename);
} else {
let URL = window.URL;
let downloadUrl = URL.createObjectURL(blob);
if (filename) {
let a = document.createElement('a');
if (typeof a.download === 'undefined') {
window.location.href = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
}
} else {
window.location.href = downloadUrl;
}
// cleanup
setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100);
}
}
I've this code to generate a download from javascript but I can't/don't know how to make it work on mobile devices.
$('#export').on("click", function (e) {
var Datos = {
"imagen": "asdfASDF123",
"puntos": [{"a":"1"}, {"b":"2"}, {"c":"3"}]
};
stringy = JSON.stringify(Datos);
var blob = new Blob([stringy], {
type: "text/json"
});
var uri = URL.createObjectURL(blob);
var link = document.createElement("a");
link.download = 'datos.json';
link.href = uri;
document.body.appendChild(link);
fireEvent(link, "click");
document.body.removeChild(link);
delete link;
});
Is there something bad in this code? or is there another method to generate a download from javascript that works on mobile devices?
I need to be able to save a string into a local file. Based on the code in here I got the following going:
function saveTextAsFile(fileNameToSaveAs, textToWrite) {
var textFileAsBlob = new Blob([textToWrite], {
type: 'text/plain'
});
var downloadLink = document.createElement("a");
downloadLink.download = fileNameToSaveAs;
downloadLink.innerHTML = "Download File";
if (true) { //window.webkitURL !== null) {
// Chrome allows the link to be clicked
// without actually adding it to the DOM.
downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
} else {
// Firefox requires the link to be added to the DOM
// before it can be clicked.
downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
downloadLink.onclick = destroyClickedElement;
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
}
downloadLink.click();
}
This works fine for Chrome and Firefox, but not for Internet Explorer 10 as
downloadLink.click();
gives:
SCRIPT5: Access is denied.
Is there any explanation/solution to this ?
thanks!
IE 10 and 11 use a distinct syntax for downloading or saving blobs to the client machine. Once you've created a blob, use:
window.navigator.msSaveBlob(blob, 'file.txt');
or
window.navigator.msSaveOrOpenBlob(blob, 'file.txt');
to trigger the file save or file save/open dialog.
For more info, see http://msdn.microsoft.com/en-us/library/ie/hh673542(v=vs.85).aspx
Thx to mstubna, here is a solution for Chrome, FF, and IE>9:
function saveTextAsFile(fileNameToSaveAs, textToWrite) {
/* Saves a text string as a blob file*/
var ie = navigator.userAgent.match(/MSIE\s([\d.]+)/),
ie11 = navigator.userAgent.match(/Trident\/7.0/) && navigator.userAgent.match(/rv:11/),
ieEDGE = navigator.userAgent.match(/Edge/g),
ieVer=(ie ? ie[1] : (ie11 ? 11 : (ieEDGE ? 12 : -1)));
if (ie && ieVer<10) {
console.log("No blobs on IE ver<10");
return;
}
var textFileAsBlob = new Blob([textToWrite], {
type: 'text/plain'
});
if (ieVer>-1) {
window.navigator.msSaveBlob(textFileAsBlob, fileNameToSaveAs);
} else {
var downloadLink = document.createElement("a");
downloadLink.download = fileNameToSaveAs;
downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
downloadLink.onclick = function(e) { document.body.removeChild(e.target); };
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
downloadLink.click();
}
}
For modern browsers solution goes like this, tested: IE11, FF & Chrome
var csvData = new Blob([arg.data], {type: 'text/csv;charset=utf-8;'});
//IE11 & Edge
if (navigator.msSaveBlob) {
navigator.msSaveBlob(csvData, exportFilename);
} else {
//In FF link must be added to DOM to be clicked
var link = document.createElement('a');
link.href = window.URL.createObjectURL(csvData);
link.setAttribute('download', exportFilename);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
plus for ie EDGE:
var ieEDGE = navigator.userAgent.match(/Edge/g);
if (ie || ie11 || ieEDGE) {
if (ieVer>9 || ieEDGE) {
var textFileAsBlob = new Blob([textToWrite], {
type: 'text/plain'
});
window.navigator.msSaveBlob(textFileAsBlob, fileName);
} else {
console.log("No supported on IE ver<10");
}
} else { ... }
You can use FileSaver library to achieve that. Easy to use, takes care of browser types and versions.
There is an AngularJS version available as well.