How to ask for permission to download files on NextJS? - node.js

I have the following endpoint:
baseurl.com/api/v1/datasheet/format
Accesing this url will download specific information based on the datasheet. In Express, for example, I return this method if the format = json:
const generateJSON = (res, data) => {
res.header('Content-Type', 'application/json');
res.attachment('example.json');
return res.send(data);
}
This works fine, but it downloads automatically the file as "example.json" without asking the user for his permission to download the file
My purpose is to have a button on my frontend (using NextJS) that will ask the user if he wants to download the file (the classic download or cancel prompt for downloads) and then open the file explorer, etc.
How can I do this? Is this something from the backend or frontend?

Use Content-disposition: attachment; filename=example.json to trigger a Save as DIalog box.
const generateJSON = (res, data) => {
res.header('Content-Type', 'application/json');
res.header('Content-disposition', 'attachment');
res.attachment('example.json');
return res.send(data);
}
The first parameter in the HTTP context is either inline (default value, indicating it can be displayed inside the Web page, or as the Web page) or attachment (indicating it should be downloaded; most browsers presenting a 'Save as' dialog, prefilled with the value of the filename parameters if present).
via https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#:~:text=The%20first%20parameter,parameters%20if%20present).

Related

Node + Express + PDF getting downloaded instead of opening

Although it may be a duplicate question but I have done everything I can without getting the solution.
I'm using Node + Express.
We use to send link of PDF file as CDN url in SMS
The link looks like this:
/api/v1/cdn/pdf/24
To get PDf and return, I have this code:
let parameter = await this.parameterDB.readRecord(where);
if (parameter) {
let fileName = parameter.id + '_' + parameter.patient_id + '.pdf';
// METHOD 1
let data = fs.readFileSync(constants.PATH.PDFS + fileName);
res.setHeader('Content-disposition', 'inline; filename="' + fileName + '"');
res.contentType('application/pdf');
res.send(data);
// METHOD 2
res.writeHead(200, {
'Content-Type': 'application/pdf',
'Content-Disposition': 'inline; filename=report_' + fileName,
'Content-Length': data.length
});
res.end(data, 'binary');
// METHOD 3
let file = fs.createReadStream(constants.PATH.PDFS + fileName);
var stat = fs.statSync(constants.PATH.PDFS + fileName);
res.setHeader('Content-Length', stat.size);
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'inline; filename=' + fileName);
file.pipe(res);
} else {
return this.responseUtil.sendReadResponse(req, res, {
message: 'not found'
}, 200);
}
I all of the above 3 methods, when I click on the link in SMS, it downloads the PDF file instead of opening in web browser (which is annoying for the customers).
File is returned properly.
What can I do here?
I assume, by sending the API link in SMS means you want your customers to view the PDF in mobile browser.
Browsers can view the PDF file due to the plugins they support or have installed by default, but this is not the case with phone browsers, atleast
not all browsers on smartphones support PDF viewer, especially android. I can view PDF files in safari and chrome browser on iPhone though, because they have support for PDF view.
source: https://www.quora.com/Why-does-Chrome-on-Android-not-open-PDF-files-like-Chrome-on-Windows-Linux-can
Any one of the method in your code can show the PDF file on desktop browser with no modification, however when you try to call the API URI on phone browser,
you'll see the PDF file getting downloaded as there is no support for that in the browser, instead browser looks for the native application on phone,
downloads the file and opens in it.
Although there is a way/work-around for this if you want to see the PDF files in the browser using google docs.
You can simply embed your URL location of the PDF in the google docs link itself and redirect to it when customer hits your API URI.
source: How to display a PDF via Android web browser without "downloading" first
So you can modify your code something like this:
let parameter = await this.parameterDB.readRecord(where);
if (parameter) {
res.redirect("https://docs.google.com/gview?embedded=true&url=http://host...your-pdf-path.pdf")
} else {
return this.responseUtil.sendReadResponse(req, res, {
message: 'not found'
}, 200);
}
There is an another alternative for this, although it would require more efforts, but it is worth if you have lots of documents in your application that need to be seen on phone browser.
You can make use of PDF.js by Mozilla and create your own custom pdf viewer, this viewer is completely created in vanilla JavaScript and thus, it is supported by almost all browsers.
Once you have your own viewer implemented, all you have to do is :
res.sendfFile('show an html file eg index.html', { pdfURL: <path of the pdf file> });
and inside this index.html file you need to pass the path of the pdf file, make use of PDF.js functions to get the document data and create custom viewer.
You can use ejs to achieve this in Express JS.

Intercept document download with Puppeteer and Extract CSV Data

I would like to download a .csv file from the browser, intercept it, and extract the data to convert it into a JSON object. Most responses say to use the requests.buffer(), however, my situation is unique as it always says the buffer is empty, but the file downloads.
I have tried to pull requests.buffer()
downloadPage.on('request', request => {
console.log(request.isNavigationRequest());
console.log(nextRequest);
if (request.isNavigationRequest() && !nextRequest) {
return request.abort();
}
initialRequest = false;
request.continue();
});
downloadPage.on('response', async (response) => {
console.log(response.buffer());
file_data = JSON.parse(response.buffer());
})
await Promise.all([
downloadPage.goto('https://clients.messagelabs.com/Tools/Track-And-Trace/DownloadCsv.ashx?sessionid=' + json_data.request.SessionId).catch(err => console.log(err)),
page.waitForNavigation()
])
Since I am on a corporate network, it won't let me upload my images, but...
When I proceed to the link above on downloadPage.goto(...) it automatically begins a .csv file download. Than the page closes. I think the page closing is clearing the buffer, however, I can't seem to intercept the response to grab the file data before this happens. Any ideas are appreciated.
Please do not link me to another github that tells me to use the request.buffer(), as I have tried many variations.
Error: Protocol error (Network.getResponseBody): No data found for resource with given identifier

Microsoft Graph API unable to update Excel file

I have an Excel file (.xlsx), which when I have it already in my OneDrive, I can use a REST command like this, to modify it:
/v1.0/me/drive/root:/SpreadSheetName.xlsx:/workbook/worksheets/content_stats/tables('RawStats')/Rows
When I modify my code to first upload the file to OneDrive (rather than using the file that is already there), and I use the REST API, I get the error:
Open navigation properties are not supported on OpenTypes. Property name: 'tables'.
I have searched the web for this message, and cannot find anything related to what I am doing. The REST call for modifying the file which was just uploaded is nearly identical, although I do reference the file by ID instead, as that is what is returned by the upload API. This is the URL I use to modify the file which was just uploaded.
/v1.0/me/drive/items:/<RealExcelSpreadsheetID>:/workbook/worksheets/content_stats/tables('RawStats')/Rows
Both are doing a POST. Exact same file, only difference is it is being uploaded first, rather than already being in OneDrive. The file was definitely uploaded correctly, as when I go through the OneDrive web interface I do find it and can view it online. This is a business account.
It was uploaded as MIME type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.
It uses these scopes delegated via OAuth2:
User.Read
User.ReadWrite
Files.Read
Files.ReadWrite
Files.ReadWrite.All
Sites.ReadWrite.All
Using Node.js and JavaScript, although that should't matter.
Here is the code used to upload the file:
function copyTemplateInOneDrive(res, queryParam, officeAccessToken, callback) {
var fs = require('fs');
var excelExt = ".xlsx";
var excelSpreadsheetFilenameStart = "stats";
var uploadUrl = "https://graph.microsoft.com/v1.0/me/drive/root:/" +
excelSpreadsheetFilename + dateNowFull() + excelExt + ":/content";
var xlsxMimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
fs.readFile(excelSpreadsheetTemplateFilename, function read(error, fileContent) {
var request = require('request');
var options = {
url: uploadUrl,
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer ' + officeAccessToken,
'Content-Type': xlsxMimeType,
},
body: fileContent
};
request.put(options, function (error, response, body) {
var result = JSON.parse(body);
if ('id' in result) {
console.log("Successfully uploaded template file to OneDrive");
res.write("Successfully uploaded template file to OneDrive");
var excelSpreadsheetID = result.id;
excelSpreadsheetUrl = result.webUrl;
} else {
console.log("ERROR: unable to upload template file to OneDrive " + body);
res.write("Error: unable to upload template file to OneDrive" + body);
return;
}
callback(null, res, queryParam, officeAccessToken, excelSpreadsheetID);
});
});
}
It uses the async module from node.js (which makes use of callback). It also saves the ID returned, and later passes it into the call to the Microsoft Graph.
Your issue here is the URL you're calling with the id is using syntax expecting the file path (i.e. folder\filename.ext) rather than the id. This is why switching to the file name started working for you.
There are two ways to address a file stored in OneDrive:
drive/items/{item-id}
/drive/root:/path/to/file (note the :)
You correctly switched your URI from drive/root to drive/items but by leaving the : in place you are telling OneDrive to address the file by it's path rather than it's id. In other words, it's looking for a file named "{some-id}".
For addressing a file by it's path, your URL is correct:
/drive/root:/{file-path}:/workbook/worksheets/content_stats/tables('RawStats')/Rows
For addressing a file by it's id however, you need to drop the ::
/drive/items/{file-id}/workbook/worksheets/content_stats/tables('RawStats')/Rows
You can read about how files are addressed in the documentation for DriveItem.

Get MIME type of Node Request.js response in Proxy - Display if image

I’m writing some proxy server code which intercepts a request (originated by a user clicking on a link in a browser window) and forwards the request to a third party fileserver. My code then gets the response and forwards it back to the browser. Based on the mime type of the file, I would like to handle the file server's response in one of two ways:
If the file is an image, I want to send the user to a new page that
displays the image, or
For all other file types, I simply want the browser to handle receiving it (typically a download).
My node stack includes Express+bodyParser, Request.js, EJS, and Passport. Here’s the basic proxy code along with some psuedo code that needs a lot of help. (Mia culpa!)
app.get('/file', ensureLoggedIn('/login'), function(req,res) {
var filePath = 'https://www.fileserver.com/file'+req.query.fileID,
companyID = etc…,
companyPW = etc…,
fileServerResponse = request.get(filePath).auth(companyID,companyPW,false);
if ( fileServerResponse.get('Content-type') == 'image/png') // I will also add other image types
// Line above yields TypeError: Object #<Request> has no method 'get'
// Is it because Express and Request.js aren't using compatible response object structures?
{
// render the image using an EJS template and insert image using base64-encoding
res.render( 'imageTemplate',
{ imageData: new Buffer(fileServerResponse.body).toString('base64') }
);
// During render, EJS will insert data in the imageTemplate HTML using something like:
// <img src='data:image/png;base64, <%= imageData %>' />
}
else // file is not an image, so let browser deal with receiving the data
{
fileServerResponse.pipe(res); // forward entire response transparently
// line above works perfectly and would be fine if I only wanted to provide downloads.
}
})
I have no control over the file server and the files won't necessarily have a file suffix so that's why I need to get their MIME type. If there's a better way to do this proxy task (say by temporarily storing the file server's response as a file and inspecting it) I'm all ears. Also, I have flexibility to add more modules or middleware if that helps. Thanks!
You need to pass a callback to the request function as per it's interface. It is asynchronous and does not return the fileServerResponse as a return value.
request.get({
uri: filePath,
'auth': {
'user': companyId,
'pass': companyPW,
'sendImmediately': false
}
}, function (error, fileServerResponse, body) {
//note that fileServerResponse uses the node core http.IncomingMessage API
//so the content type is in fileServerResponse.headers['content-type']
});
You can use mmmagic module. It is an async libmagic binding for node.js for detecting content types by data inspection.

How to open automatically downloaded Word document in the browser?

I use Aspose to generate a Word document. It must be opened in the browser automatically when it comes back from the server.
Here is my code:
Do Ajax call to get the document
$.ajax({
url: "Export/StreamWord",
data: { topicId: CurrentTopic.id },
success: function (result) {
//Nothing here. I think that the browser must open the file automatically.
}
});
Controller .NET MVC 3
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult StreamWord(string topicId)
{
var stream = new MemoryStream();
Document doc = exportRepos.GenerateWord(topicId); //Document is a Aspose object
doc.Save(stream, SaveFormat.Docx);
stream.WriteTo(Response.OutputStream);
return File(stream, "application/doc", "test.doc");
}
BUT when I run it from the browser nothing happen.
Response from the server you can see on the image. Document comes, but it is not been opened.
Any suggestions?
Do not use AJAX for this, just use a simple page redirect instead. If you use a page redirect it will prompt the user to download the file, it won't actually move them away from the current page.
The code would look like
document.location.href = "Export/StreamWord?topicId=" + CurrentTopic.Id;
What you're attempting is not possible with AJAX.

Resources