puppeteer fileChooser: how to use URL instead of local file path? - node.js

I have the http link of a video file (cloud storage) and I want to select that file in filechooser.
I am using cloud hosting (glitch), so I don't want to store that file to local storage.
const [fileChooser] = await Promise.all([
page.waitForFileChooser(),
page.click('#select-files-button')
]);
await fileChooser.accept(["https://examle.mp4"]);
It only seems to accept a local file path, can anyone help me?

As you could have guessed, it obviously accepts only local files, you can download your desired file first (with request module or even http(s?) module), then pass it to puppeteer.

Related

Is there a way to save a file to Heroku from a click via Puppeteer - NodeJs

I am using NodeJs & Puppeteer to access a web page and click a button which downloads a csv file. Locally this all works fine - I assign a path for the download and it saves it into the targeted folder. However, when running this deployed through Heroku, I can see that the Puppeteer script runs yet no file is downloaded. I assume this is an issue with saving files to Heroku. I realise Heroku uses ephemeral hard disks and I am fine with the csv file being deleted in a short period of time, I just need to read the data, handle it & then pass it to the frontend.
I have this bit of code which saves the csv file to my local folder destination.
const client = await page.target().createCDPSession();
await client.send('Page.setDownloadBehavior', {
behavior: 'allow',
downloadPath: downloadLocation, // Set download location
});
const csvButtonName = '.widget-actions-item';
await page.waitForSelector(csvButtonName);
await page.hover(csvButtonName);
await page.click(csvButtonName); //Downloads the csv file
I can my same directory structure on Heroku but cannot find the attempted csv download file.
Any help is appreciated.

Possible to use cloud function to download video onto Firebase server?

I want to use write a cloud function to download a video file. I will do something like below to save the file into a mp4 format file.
const http = require('http');
const fs = require('fs');
const file = fs.createWriteStream("video.mp4");
const request = http.get("http://my-video-server.com/nice-day.mp4", function(response) {
response.pipe(file);
});
But I am wondering where is the file saved? Is it somewhere under /tmp on the firebase server? Can I specify the path for downloading the file? Also, if the video size is several GB, does firebase has any limit about how we use your server?
The reason I want to download a video file is I want to use the file to upload to YouTube via Youtube's API. They don't support upload via a link, it has to be a physical file.
The only writable path in Cloud Functions is /tmp. It's a memory-based filesystem, so you will only have a very limited amount of space available. I suggest reading the documentation on the Cloud Functions execution environment.
You can specify the exact file of the download using the API of the modules you're using for the download.
Cloud Functions is not a suitable product for work that requires large amount of disk space or memory.

createReadStream doesn't take blob url?

this.state.videoBlob is a blob object. I used URL.createObjectURL to generate a blob URL, and passed it to fs.createReadStream, like below:
fs.createReadStream(URL.createObjectURL(this.state.videoBlob))
This blob url looks like:
blobURL: blob:http://localhost:3000/dabe5cdd-00cc-408a-9f3d-b0ba5f2b10b3
But I got an error saying:
TypeError: fs.createReadStream is not a function
The problem won't exist if I passed some online video URL. So how can I read blob from fs.createReadStream? Thanks!
When I look at the code behind fs.createReadStream(), it calls new ReadStream() and passes the path/url to that. On this line of code, it appears that the only type of url that is supported is a file URL. The doc is silent on that topic which is why I went and looked at the code. So, it does not appear to me that fs.createReadStream() supports that type of pseudo-URL.
Since you just want a readstream from that URL and you have the actual URL of the remote resource, I would suggest you just use either http.get() or request() or something similar as those will all contact the remote host and return to you a readStream. Since your objective was to get a readStream, this is one way to achieve that.
http.get('http://localhost:3000/dabe5cdd-00cc-408a-9f3d-b0ba5f2b10b3', (res) => {
// res is a readstream here
}).on('error', (err) => {
// error on the request here
});
FYI, you may find this answer on Blob URLs to be useful. I don't see any evidence that fs.createReadStream() supports blob URLs. In the browser, they are something created only by the internals of the browser and are only useful within that specific web page context (they refer indirectly to some internal storage) and can't be passed outside the web page or even preserved from one web page to the next. If you wanted your server to have access to the actual data from a blob URL created in the browser, you'd have to upload the actual data to your server. Your server can't access a blob URL created in the browser.

Piping a file straight to the client using Node.js and Amazon S3

So I want to pipe a file straight to the client; how I am currently doing it is create a file to disk, then sending that file straight to the client.
router.get("/download/:name", async (req, res) => {
const s3 = new aws.S3();
const dir = "uploads/" + req.params.name + ".apkg"
let file = fs.createWriteStream(dir);
await s3.getObject({
Bucket: <bucket-name>,
Key: req.params.name + ".apkg"
}).createReadStream().pipe(file);
await res.download(dir);
});
I just looked up that res.download() only serves locally. Is there a way you can do it directly from AWS S3 to Client download? i.e. pipe files straight to user. Thanks in advance
As described in this SO thread:
You can simply pipe the read stream into the response instead of the piping it to the file, just make sure to supply the correct Content-Type and to set it as an attachment, so the browser will know how to handle the response properly.
res.attachment(req.params.name);
await s3.getObject({
Bucket: <bucket-name>,
Key: req.params.name + ".apkg"
}).createReadStream().pipe(res);
On more pattern for this is to create a signed url directly to the S3 object and then let the client download straight from S3, instead of streaming it from your node webserver. This will reduce the workload from your web server.
You will need to use the getSignedUrl method from the AWS S3 SDK for JS.
Then, Once you have the URL, just return it to your client to download the file by themselves.
You should take into account that once you give the client a signed URL that has download permissions for, say, 5 minutes, they will only be able to download that file during those next 5 minutes. And you should also take into account that they will be able to pass that URL to anyone else for download during those 5 minutes, so it is dependant on how secure you need this to be.
S3 can be used to content so I would do the following.
Add CORS headers on your node response. This will enable browser to download from another origin i.e. S3.
Enable S3 web server on your bucket.
Script to download redirect from S3 - this you could achieve in JS.
Use signed URL as suggested in the other post if you need to protect S3 content.

Node/Express: File not downloading using fs.pipe()

I'm having an issue downloading files to my local machine using fs.pipe() in my Node.js/Express cloud foundry application.
Basically, a POST request is sent to my server side code containing the file name my user wants to download. I access the file using the GET command from the npm module ssh2-sftp-client. Finally, this file gets saved to the users local downloads folder using the npm module downloads-folder to identify this location. The code looks like this:
app.post('/download-file', function(req, res) {
// Declare the files remote and local path as a variable.
const remoteFilename = 'invoice/csv/' + req.body.file;
const localFilename = downloadsFolder() + '/' + req.body.file;
// Use the SFTP GET command to get the file passing its remote path variable.
sftp.get(remoteFilename).then((stream) => {
// Download the file to the users machine.
stream.pipe(fs.createWriteStream(localFilename));
// Redirect user.
res.redirect('/invoice')
});
})
This works perfectly when running locally and the file gets downloaded with no issues. As this screenshot shows, the output for the destination file path is this:
However, when I push this to our cloud foundry provider using cf push, the application still works fine but when I want to download the file it fails. I get no errors when error catching, the only thing thats changed is that the output for the destination file path has changed to:
I have no idea why this is, this code works fine in Chrome, Safari when running locally but when hosted doesn't do anything. Can anyone explain what's going wrong here?
Many thanks,
G
// Download the file to the users machine.
It doesn't do that, though: it downloads the file to the machine on which the server code is running. That's why it seems to work when you run the server on localhost, because the server machine and the user machine are the same.
Offering the file as a download involves streaming the file through the response object, making sure that you set the correct header. Something like this:
sftp.get(remoteFilename).then((stream) => {
res.set('content-disposition', `attachment; filename="${ req.body.file }"`);
stream.pipe(res);
});
When offering a file as "attachment", it typically opens the download window of the browser and the browser stays on the same page. You can't both stream and perform a redirect, but because the browser doesn't change the URL, that should be okay.
Also, you have no control over where the user will download the file to. It may be the downloads folder, but they are free to chose another folder.

Resources