Downloading files using vue.$http not working on chrome mobile - google-chrome

I have a function in my vue that I use to download files from the firebase cloud storage. The functions look like:
forceFileDownload(response, issue, index){
const increment = firebase.firestore.FieldValue.increment(1)
db.collection('issues').doc(this.ids[index]).update({
downloads: increment
})
var extension = mimeTypes.extension(response.headers['map']['content-type'][0]);
const url = window.URL.createObjectURL(new Blob([response.data]))
const link = document.createElement('a')
link.href = url
link.setAttribute('download', issue['name']+"."+extension) //or any other extension
document.body.appendChild(link)
link.click()
},
downloadWithVueResource(issue, index) {
this.$http({
method: 'get',
url: issue['path'],
responseType: 'arraybuffer'
})
.then(response => {
this.forceFileDownload(response, issue, index)
})
.catch(() => console.log('error occured'))
},
As you can see above I am using vue-resource to download the file. The reason I am doing this instead of binding the link to an anchor tag is because the file is dynamically rendered using a v-for as follows:
<div v-for="(issue, index) in issues" :key="index">
<button #click="downloadWithVueResource(issue, index)">Download</button>
</div>
This works fine on my ubuntu chrome and on safari on my phone. But it's not working in chrome on my phone. I don't understand why this is happening. Any help would be appreciated. Thanks

You can check Chrome Download Attribute not working for potential solutions for the download attribute. You can also check what version you have and whether it supports the download attribute here.
You said that the reason you're using vue-resource is because
The reason I am doing this instead of binding the link to an anchor tag is because the file is dynamically rendered using a v-for
You should be able to use an anchor tag. The only reasons (that I'm aware of) that you can't use an anchor tag for downloads are if there's some authentication on your backend that looks at an authorization header for example or you would want to do some custom javascript, like a progress bar for example.
You can bind the href to your path property.
<div v-for="(issue, index) in issues" :key="index">
<a :href="issue.path" #click="logDownload(issue, index)">Download</a>
</div>
Then log the download on click:
logDownload(issue, index)
{
const increment = firebase.firestore.FieldValue.increment(1);
db.collection('issues').doc(this.ids[index]).update({
downloads: increment
});
}

Related

Cypress e2e testing: How to access new tab by clicking on "href". I don't have target attribute to remove and test on new opened tab?

This attached image is my HTML code for href which will open a new tab
The DOM has iframe so I wrote below code accessed the href and it will open in new tab. I am unable to access the newly opened tab, though I know the method that would have target attribute so we remove that and open in same tab but here I don't have any target attributes.
Please check this and help to access my new tab.
cy.visit('https://yopmail.com/en/')
cy.get('.ycptinput').type('some_name {enter}')
cy.wait(2000)
cy.get('#ifmail').its('0.contentDocument.body').then(cy.wrap).find('a').click()
The cy.origin() command is meant to solve the "new tab" problem.
It's a bit new, so expect some teething problems. Basically, it sets up a sand-boxed domain that can use Cypress commands.
Anything from outside cy.origin() that you want to use inside (for example, the link you found) needs special handling to pass in.
It gets passed in on a special args option, and is received in the same pattern.
let link;
cy.visit('https://yopmail.com/en/')
cy.get('.ycptinput').type('some_name {enter}')
cy.wait(2000)
cy.get('#ifmail').its('0.contentDocument.body')
.then($body => {
link = $body.find('a')[0].href
})
cy.then(() => { // this just waits for above block to complete
const newOrigin = link.split('?')[0] // remove query params
.replace('http://', 'https://') // correct for secure protocol
cy.origin(newOrigin, { args: { link } }, ({ link }) => {
cy.visit(link) // same as ".find('a').click()" but works cross-domain
})
})

Open PDF in new Tab - Blazor

I would like to open a PDF in a new Tab. The following code works in a normal HTML File, but not in Blazor. Is there any way to use it in Blazor or is there another way to open a PDF in Blazor?
Read more
What works in "normal" HTML, does not work in Blazor, because the service-worker routes unknown routes to index.html. See onFetch() method of service-worker.published.js:
const shouldServeIndexHtml = event.request.mode === 'navigate';
const request = shouldServeIndexHtml ? 'index.html' : event.request;
When you click your link Read more, the shouldServeIndexHtml is true.
To avoid this, simply change above code to
const shouldServeIndexHtml = event.request.mode === 'navigate'
&& !event.request.url.includes('/pdf/');
const request = shouldServeIndexHtml ? 'index.html' : event.request;
This assumes, that your pdfs are in a subfolder "pdf" under wwwroot. Please find more information here. Since the url includes "/pdf/", the shouldServeIndexHtml becomes false and your pdf is displayed.
Two options I can suggest:
Via a button.
#inject IJSRuntime jsRuntime
<button #onclick="#LoadPage">Load Page</button>
#code {
async Task LoadPage()
{
await jsRuntime.InvokeAsync<object>("open", "MyPage", "_blank");
}
}
Via a link.
<a class="dropdown-item" href="MyPage" target="_blank">
where the razor page you want to launch has the following at the top:
#page "/MyPage"

How to download a file uploaded on google cloud storage instead of viewing it?

I upload a file on google cloud storage and sends the url to an ejs file in which i assign its url to a button. When i click on the button, I want to download the file instead of viewing it in browser. How can i make it possible?
Ejs file code
<div class="send-btn-container">
<a
href="<%= downloadLink %>"
download
target="_blank"
type="application/octet-stream"
>Download file</a>
</div>
As of late 2018, clicking the link won’t trigger a download if the resource to be downloaded wasn’t served from the same origin or same server so to say. Apparently, this restriction is a security measure.
I am using my react code as example to show you a way to download a file from GCloud storage.
const download = (e) => {
e.preventDefault();
// Remote URL for the file to be downloaded
const url = 'https://storage.googleapis.com/...';
const filename = "mypdf.pdf";
fetch(url)
.then((response) => response.blob())
.then((blob) => {
const blobURL = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = blobURL;
a.style.display = 'none';
if (filename && filename.length) a.download = filename;
document.body.appendChild(a);
a.click();
})
.catch((error) => {
console.error(error);
});
};
<a onClick={(e) => download(e)}>
Download file
</a>
convert into a blob URL instead.
I am answering this in regard to HTML5. If you want the file to be downloaded instead of viewing it, you can add download attribute to <a> tag as follows:
<a href="downloadLink.pdf"
download="mypdf.pdf" <!-- This will force download the file -->
target="_blank"
type="application/octet-stream" </a>
In this case, the file downloadLink.pdf will be downloaded as mypdf.pdf but this attribute is currently supported in chromium browsers only.

How to add scraped data with puppetteer into html

ADVISORY : I'm trying my hands at this for the first time.
I created an html page that displays bus timings. To get the bus timings I had to scrape the local bus service website with puppeteer. I do scrape the time for the next bus correctly, but I can't seem to add it to my html page.
I've tried adding the script tags with src pointing to my js file. I tried adding them to the head, in the div that should display the time and right before the closing body tag, but I couldn't display the time. I event tried to add the js in a script tag to the html and that didn't work.
//Here's code for scraping in busTimeScraper.js :
let scrape = async() => {
const browser = await puppeteer.launch({
headless: true
});
const page = await browser.newPage();
await page.goto('bustimes.com'); //Dummy website for this eg
await page.setViewport({width: 1500, height: 1500})
await page.waitFor(5000);
const result = await page.evaluate(() => {
let time = document.querySelector('#RouteTimetable').innerText;
return {
time
}
});
browser.close();
return result;
};
scrape().then((value) => {
var timing = value.time;
document.querySelector('#Time').innerText=timing;
});
//The html is :
<div id="Time">
<!--<script type="text/javascript" src="busTimeScraper.js">
</script>-->
</div>
I can see the time being scraped when I run the js file and do a console.log on the timing variable. I expected the div to be populated with the same time value, but it just stays blank
You simply cannot add your server-side JS in your client-side html using a script tag and expect it to work, no matter where you add (in head, inside element or before closing body);
Simplest solution would be to expose the result (timing variable) VIA NodeJsAPI and consume the API VIA your client-side JS to get the value and do rest of the client-side things.

Inject HTML into a page from a content script

I am building a Chrome Extension and I have a requirement to overlay a blob of html on top of a few websites. At the moment I am using a JQuery .Get to pull the html from my server. In order to improve performance I am wondering if it is possible to include the html as a file in the extension directory and access the source directly from there? Does anyone know if this is possible?
UPDATE
Rob's suggestion does the job (see accepted answer). The only additional step is to register the file in the manifest under web_accessible_resources.
{
...
"web_accessible_resources": [
"myimportfile1.html",
"myimportfile2.html"
],
...
}
Yes, that's possible. Use chrome.runtime.getURL to get an absolute URL for the resource. For example:
Step 1 (standard JavaScript):
fetch(chrome.runtime.getURL('/template.html')).then(r => r.text()).then(html => {
document.body.insertAdjacentHTML('beforeend', html);
// not using innerHTML as it would break js event listeners of the page
});
Step 1 (jQuery):
$.get(chrome.runtime.getURL('/template.html'), function(data) {
$(data).appendTo('body');
// Or if you're using jQuery 1.8+:
// $($.parseHTML(data)).appendTo('body');
});
Step 2:
Register the resource in the manifest.json under web_accessible_resources:
"web_accessible_resources": [
"template.html",
"foo.jpg"
]
Another way of doing it is to use new Fetch API:
If the file's name is modal.html - update manifest.json accordingly
"web_accessible_resources": [
"modal.html",
],
and inject it like this:
fetch(chrome.runtime.getURL('/modal.html'))
.then(response => response.text())
.then(data => {
document.getElementById('inject-container').innerHTML = data;
// other code
// eg update injected elements,
// add event listeners or logic to connect to other parts of the app
}).catch(err => {
// handle error
});
This is my approach using a synchronous XHR:
var xmlHttp = null;
xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", chrome.runtime.getURL ("src/inject/inject.html"), false );
xmlHttp.send( null );
var inject = document.createElement("div");
inject.innerHTML = xmlHttp.responseText
document.body.insertBefore (inject, document.body.firstChild);
Without jQuery etc.
I use this code. It's only 3 lines of code and you don't need any jquery's garbage.
var iframe = document.createElement ('iframe');
iframe.src = chrome.runtime.getURL ('iframe.html');
document.body.appendChild (iframe);
If you're using Angular in your Chrome extension, you can make use of ng-include
var injectedContent = document.createElement("div");
injectedContent.setAttribute("ng-include", "");
//ng-include src value must be wrapped in single quotes
injectedContent.setAttribute("src", "'" + chrome.runtime.getURL("template.html") + "'");
existingElement.appendChild(injectedContent);