Angular/Javascript - Download file link is not working on mobile devices - javascript

I have a href link to download a template from my application. It is working fine in Chrome and IE but however it is not working in mobile devices( Android as well as iPhone)
I have this function which gets called by clicking on the link..
fileUrl: any;
getFileTemplate(): any {
this.productService.getFile().subscribe((response) => {
const fileContent = response;
// An application or a document that must be opened in an application
const blob = new Blob([fileContent], { type: 'application/octet-stream' });
if (window.navigator.msSaveBlob) {
// to download in IE
window.navigator.msSaveBlob(blob, 'abcd.csv');
} else {
this.fileUrl= this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(blob));
const a = document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = 'abcd.csv';
a.click();
}
});
}
And in HTML file
`<a href="javascript:void(null)"
(click)="getFileTemplate();"
id="link-inline-excel"
class="u-text--document u-text--document-link"
download="abcd.csv"><span>Title my file (7MB)</span></a>`
This is not working in mobile devices.
Am i missing something here?

You are more or less on the right track. The minimum modification I can offer to get your code to work is adding one more line to getFileTemplate:
getFileTemplate(): any {
this.productService.getFile().subscribe((response) => {
const fileContent = response;
// An application or a document that must be opened in an application
const blob = new Blob([fileContent], { type: 'application/octet-stream' });
if (window.navigator.msSaveBlob) {
// to download in IE
window.navigator.msSaveBlob(blob, 'abcd.csv');
} else {
this.fileUrl= this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(blob));
const a = document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = 'abcd.csv';
document.body.appendChild(a); //<-- Need to add the link to the DOM
a.click();
}
});
}
Of course, while this works, it's not a very clean solution. For one, the user will be able to see the newly appended link. For another, the Angular documentation suggests avoiding direct DOM manipulations, using Renderer2 instead.
Here's a StackBlitz example with both of these things in mind.
The example creates a separate Downloader component, that acts like a basic anchor element, but encapsulating the logic to trigger a download. You can then use this component wherever you want to trigger a file download.
The gist of the answer is this snippet:
...
constructor(
private element: ElementRef,
private renderer: Renderer2
) {}
...
download(data: Blob, filename: string) {
if(!data) {
return;
}
//Create the anchor element
const link: any = this.renderer.createElement('a');
//Create the URL
const url: any = URL.createObjectURL(data);
//Set the attributes for the anchor
this.renderer.setProperty(link, 'href', url);
this.renderer.setProperty(link, 'download', filename);
//Ensure that the anchor will be hidden, both visibly and from screen readers
this.renderer.setStyle(link, 'display', 'none');
//Add the anchor element to the DOM
this.renderer.appendChild(this.element.nativeElement, link);
//Trigger click on the anchor element to trigger the download
link.click();
//Cleanup by removing the element and revoking the URL.
this.renderer.removeChild(this.element.nativeElement, link);
URL.revokeObjectURL(url);
//Note: This is just a basic example, which does do DOM manipulation
//on every download. You could, instead, append the element in OnInit,
//adjusting its attributes when a download is triggered, and then
//remove the element in OnDestroy.
}

Related

Download multiple files in an angular application

I have an async method that retrieves the presigned URL of a selected file and a method that takes that URL and downloads the file by adding an anchor tag, assigns the URL to href and then simulates a click event. It works fine for a single file download. However, when there are multiple files selected it doesn't download all the files. I have tried other approaches such as calling fetch(), axios etc but that also results in the same issue.
Below is my code:
method in component:
`public async downloadSelectedFiles(event: Event, entitlementId: string) {
event.preventDefault();
for (const releaseFile of this.selectedReleaseFiles) {
const url = await this._softwareProductsService.getFileDownloadUrl(entitlementId, String(releaseFile.id));
await this._downloadService.downloadFile(url);
}
}`
method in service:
`public async downloadFile(url: string): Promise<void> {
const a = this._document.createElement('a');
a.download = '';
a.href = url;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
await this._delay(100);
document.body.removeChild(a);
}
private _delay(ms: number): Promise<void> {
return new Promise<void>((resolve) => {
setTimeout(resolve, ms);
});
}`
I have tried setting id of anchor tag. Waiting after click and after remove as well. Also tried 'multi-download' library. All result in same issue.
It doesn't download all the files when download button is clicked for the first time. When clicked again it works fine.
Is there a way to await on anchor tag click event?

Opening a PDF from $http.post crash when using _blank

I'm making a post to a WS and receive a PDF, I transform it into a blob, create a URL and open it.
If i'm opening the pdf in '_self', it works! I see the pdf.
I can also create a link with a download element and it works too!
But if i'm opening the pdf in '_blank', a new tab is created and instantly close.
Here is my code:
getDoc(id) {
const url = 'url';
this.$http.post(
url,
{
data: {
id,
},
},
{
responseType: 'arraybuffer',
}).then((response) => {
const blob = new Blob([response.data], { type: 'application/pdf;' });
const urlPdf = this.$window.URL.createObjectURL(blob);
const win = this.$window.open(urlPdf, '_blank');
win.focus();
});
}
Do I have to add something to allow it to open in a new tab/window?
Why does it works for _self but not _blank?
Thank you!
I had adblock enabled and this was the problem, once disabled the pdf is now working with _blank!
Thanks Alon Eitan for your comment!

Why isn't video recording working in my React-app?

so I am trying to incorporate a feature into a website for a client where visitors can record video on their site and then download it/upload it onto their server.
They wish for this to be built as a react component but I just started learning react last week so I'm a tad confused however it shouldn't be too complex.
So I have the following code which works in simple html/js files, outside of react where if you run it you get a little video recording screen and you can download the recording:
I also tried including the contents of de.js into the Def class component instead of importing it but this also led to the same result.
I have tried to figure out any other better easier ways to get video recording and uploading/downloading functionality into a react-component but haven't found any. I can't quite figure out how to use the popular RecordRTC library for this. Any other simple solution would be great, I'm not bound to this way of doing this, I just need to get something that works.
Any help would be GREATLY appreciated!!!
********************************** EDIT **************************************
If I run the below code:
import React, { Component } from 'react';
import './Def.css';
class Def extends Component {
render() {
const constraints = {
"video": {
width: {
max: 400
}
},
"audio": true
};
var theStream;
var theRecorder;
var recordedChunks = [];
function startFunction() {
navigator.mediaDevices.getUserMedia(constraints)
.then(gotMedia)
.catch(e => {
console.error('getUserMedia() failed: ' + e);
});
}
function gotMedia(stream) {
theStream = stream;
var video = document.querySelector('video');
video.src = URL.createObjectURL(stream);
try {
var recorder = new MediaRecorder(stream, {
mimeType: "video/webm"
});
} catch (e) {
console.error('Exception while creating MediaRecorder: ' + e);
return;
}
theRecorder = recorder;
recorder.ondataavailable =
(event) => {
recordedChunks.push(event.data);
};
recorder.start(100);
}
function stopFile() {
theRecorder.stop();
}
function download() {
theRecorder.stop();
theStream.getTracks().forEach(track => {
track.stop();
});
var blob = new Blob(recordedChunks, {
type: "video/webm"
});
var url = URL.createObjectURL(blob);
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = url;
a.download = 'test.webm';
a.click();
// setTimeout() here is needed for Firefox.
setTimeout(function () {
URL.revokeObjectURL(url);
}, 100);
}
return (
<div>
<button onClick={startFunction()} >Record</button>
<button onClick={download()}> Download!</button>
</div>
);
}
}
export default Def;
And then run What happens is that I do get a message from Chrome that asks for permission to use the webcam but nothing is visible on the screen (not even the buttons) and it is completely blank. I feel like this issue might be due to some bindings which react needs but I am not sure :(
The error log now says:
bundle.js:33208 Uncaught TypeError: Cannot read property 'stop' of undefined
at download (bundle.js:33208)
at Def.render (bundle.js:33249)
at bundle.js:26631
at measureLifeCyclePerf (bundle.js:25910)
at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (bundle.js:26630)
at ReactCompositeComponentWrapper._renderValidatedComponent (bundle.js:26657)
at ReactCompositeComponentWrapper.performInitialMount (bundle.js:26197)
at ReactCompositeComponentWrapper.mountComponent (bundle.js:26093)
at Object.mountComponent (bundle.js:18474)
at ReactCompositeComponentWrapper.performInitialMount (bundle.js:26206)
this isn't a React issue - getUserMedia isn't supported across the board.
http://caniuse.com/#feat=stream
edit: my mistake - your error message actually tells you all you need to know: Expected onClick listener to be a function, instead got type string - you're literally passing in a string as the onClick handler, i.e onClick="startFunction()" - you want it to be onClick={yourFunction}

Opening PDF from angular blob in Internet explorer browser

I have the following server side code in web api
tempResponse = Request.CreateResponse(HttpStatusCode.OK);
tempResponse.Content = new StreamContent(stream);
tempResponse.Content.Headers.Add(#"Content-type", "application/pdf");
tempResponse.Content.Headers.ContentType = new
System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
tempResponse.Content.Headers.ContentDisposition = new
System.Net.Http.Headers.ContentDispositionHeaderValue("inline");
I am using angular JS and following is the code in my javascript file.
$http.post(apiURL + "/DownloadPdf", data, { responseType: 'arraybuffer'}, config)
.then(function (result) {
var file = new Blob([result.data], { type: 'application/pdf' })
var fileName = "CommissionStatement.pdf"
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.location.href = 'Assets/Document CheckList.pdf'
window.navigator.msSaveOrOpenBlob(file, fileName)
} else {
var objectUrl = URL.createObjectURL(file)
window.open(window.location.href = 'Assets/Document CheckList.pdf', '_blank')
window.open(objectUrl, '_blank')
$window.location.href =
window.location.protocol + "//" +
window.location.host + "?BrokerId=" +
AgentInfo.Data.BrokerId +
"&OfficeCode=" +
AgentInfo.Data.OfficeCode;
}
});
console.log($scope.Result)
},
function (error) {
$scope.Error = error.data
})
This blob opens fine in Google Chrome and FireFox. But IE will prompt for open or save. But I would like it to open in the browser. I would appreciate any input in making it open without prompting. Thanks
How about just excluding the if/else statement and just open the ObjectURL in IE as well? Otherwise pdf.js is a alternative if you want to render it in a browser using canvas
Another problem I see with your code is that you are trying to open up a new window with window.open() the problem is that they can become very easy blocked unless it happens within 1 sec after a user interaction event like onclick for example. A xhr.onload is not an user interaction event
So if you are experience some issue like that try doing something like
// Untested, just to give a ruffly idea
btn.onclick = () => {
var win = window.open('', '_blank')
win.document.body.innerHTML = 'loading...'
$http.post(...).then(res => {
var url = URL.createObjectURL(blob)
// redirect
win.location.href = url
})
}
Another thing. Why are you using responseType = arrayBuffer? you could set it to a blob directly...?

pass input file to background script

I want to pass the input file from content page to extension background script, and then load it with FileReader() in the extension background script.
So in the web page I have a <input type="file"> and from onchange event I pass the file from content script to background page like this:
var myfile = document.getElementById('fileid').files[0];
chrome.runtime.sendMessage({myevent: "start", inputfile: myfile}, function(response) {});
in the background script I have this:
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse){
if(message.myevent==="start")
{
var reader = new FileReader();
reader.onload = function(e) {
// file is loaded
}
reader.readAsArrayBuffer(message.inputfile);
}
});
but FileReader not load it, I'm not sure if this is correct way , but all i need is to pass the input file element to background script and load it with FileReader to send it with HTTP POST from background script. Please tell me what is wrong or how to do it correctly. It will help a lot if I see a sample code, because I'm new to chrome extension development, and not so experienced.
All messages send through the Chrome extension messaging API MUST be JSON-serializable.
If you want to get the contents of a file at the background page, you'd better create a (temporary) URL for the File object, pass this URL to the background page and use XMLHttpRequest to grab its contents:
// Create URL
var url = URL.createObjectURL(myfile);
// Pass URL to background page (ommited for brevity) and load it..
var x = new XMLHttpRequest();
x.onload = function() {
var result = x.response;
// TODO: Use [object ArrayBuffer]
};
x.open('GET', url); // <-- blob:-url created in content script
x.responseType = 'arraybuffer';
x.send();
Though why do you want to send the file to the background page? Content scripts can also send cross-origin requests.
This works for chrome. You could find the whole production code here.
https://github.com/Leslie-Wong-H/BoostPic/tree/7513b3b8d67fc6f57718dc8b9ff1d5646ad03c75/BoostPic_Chrome/js
main.js:
// Crossbrowser support for URL
const URLObj = window.URL || webkitURL;
// Creates a DOMString containing a URL representing the object given in the parameter
// namely the original Blob
const blobUrl = URLObj.createObjectURL(imageBlob);
console.log(blobUrl);
chrome.runtime.sendMessage(blobUrl, (res) => {
imgUrl = res;
console.log(imgUrl);
clearInterval(refreshIntervalId);
// To prevent that it happens to halt at " Image uploading ..."
setTimeout(() => {
var imgUrlText = document.querySelector(imgUrlTextBoxId);
imgUrlText.value = imgUrl;
}, 1000);
// double check to clear interval to prevent infinite error loop of LoadingStateOne
// Hope it works.
setTimeout(() => {
clearInterval(refreshIntervalId);
}, 500);
console.log("Stop uploading state message");
background.js:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.startsWith("blob")) {
console.log("RECEIVED");
getBase64Url(request).then((res) => {
console.log("Arrived here");
// Acquired from https://stackoverflow.com/questions/18650168/convert-blob-to-base64/18650249#
const reader = new FileReader();
reader.readAsDataURL(res);
reader.onloadend = function () {
const base64data = reader.result;
console.log(base64data);

Categories