Showing binary PDF in browser Javascript - javascript

I have a MSSQL Server Database with files stored as a column Content of type VARBINARY(MAX) in a SQL table.
I developed a backend Node.js server with Express.js to access the database. In this backend I have a route called /api/file/:id_file which takes the content of the file with id_file
async getFile(req,res){
const {id_file} = req.query
const file= await db.Files.findOne({
where:{
'Id': id_file,
}
})
res.contentType('application/pdf')
res.send(file.Content)
}
Then in frontend using Javascript, I have an iframe
<iframe id="view" src="" style='height: 500px; width: 1000px;' />
And I use an ajax request to get the file. When I have the response, I just convert it to blob and set the src attribute of iframe element with the base64 encoding of the blob
const reader = new FileReader();
reader.onload = () => {
const b64 = reader.result.replace(/^data:.+;base64,/, "");
$('#view').attr('src','data:application/pdf;base64,'+ b64);
};
reader.readAsDataURL(new Blob([resp.data], { type: "application/pdf" }));
When I try to request a file and show, I see a blank PDF, nothing is shown.
Am I missing any step to convert the content of the file to base64?

You don't need a filereader since you're not reading a file from the filesystem or user input; you already have the file pulled down from the API. If your binary pdf data is in resp.data then you just need to use btoa
creates a Base64-encoded ASCII string from a binary string
$('#view').attr('src','data:application/pdf;base64,'+ btoa(resp.data));

Related

How to upload a client PDF file object to the pdf-lib JavaScript library

I have looked at the pdf-lib website and do not see any example to load a PDF using an "input file" on the client side. All examples points to a URL to load a PDF. Is it possible to pass a file object to the function that reads the PDF instead of a URL?
const formPDFBytes = await fetch(url).then(res => res.arrayBuffer())
const pdfDoc= await PDFDocument.load(formPDFBytes );
I think if we can somehow set the file object as an array buffer to formPDFBytes, that may do the trick.
[From OP comments]
The user is the one who has the PDF and loads it via the DOM input file dialog. So nothing here would be sent to the server and the entire PDF parsing operation is done by the client.
According to pdf-lib's code, the PDFDocument.load function accepts in the first argument a string | Uint8Array | ArrayBuffer.
If you want to read a file locally, you can do this:
import fs from 'fs'
const uint8Array = fs.readFileSync('/path/to/myFile.pdf')
const pdfDoc3 = await PDFDocument.load(uint8Array)
if you want to load the file from the <input type="file" /> element,
you need firstly to read the file as ArrayBuffer.
<input type="file" id="file">
//
const file = document.getElementById('file').files[0]
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = () => {
const pdfDoc = await PDFDocument.load(reader.result);
//...
};

How to view blob data directly using html

How do you view a blob data that is from a sql database file (.db .db3 and others) and view it on web browser by only using a single html file? The blob data are probably meant to be seen as an image file (jpg, png and others)
Let's say I have a blob data like this:
du�� C�BVwv�q8q7k�1�H�asfdasdfasdf�#s;47sk"as��'7hib-�3$asdffdsfa�a�����U�����P������
And I want to put that single blob data directly (without calling the database file, just using the value of the blob itself) inside a html file so I can directly open it from my browser without installing other software or setting up a local server inside my computer.
I'm sorry if I explain this weirdly, I rarely code, I honestly don't know anything about sql or that server thingamajig, I just want to view the blob file.
You could use Blob. Here I construct a blob and then turn it back into a string that I insert in the document.body.
var array = ['<p>Hello World!</p>'];
var blob = new Blob(array, {type : 'text/html'});
const reader = new FileReader();
reader.addEventListener('loadend', e => {
document.body.innerHTML += e.target.result;
});
reader.readAsText(blob);
And I guess that the Filereader can also read a file if needed.

Some Random characters appearing at beginning of .txt file after converting from base64 node js

So i am dropping a .txt file in an uploader which is converting it into base64 data like this:
const {getRootProps, getInputProps} = useDropzone({
onDrop: async acceptedFiles => {
let font = ''; // its not actually a font just reusing some code i'll change it later its a .txt file so wherever you see font assume its NOT a font.
let reader = new FileReader();
let filename = acceptedFiles[0].name.split(".")[0];
console.log(filename);
reader.readAsDataURL(acceptedFiles[0]);
reader.onload = await function (){
font = reader.result;
console.log(font);
dispatch({type:'SET_FILES',payload:font})
};
setFontSet(true);
}
});
Then a POST request is made to the node js server and I indeed receive the base64 value. I then proceed to convert it back into a .txt file by writing it into a file called signals.txt like this:
server.post('/putInDB',(req,res)=>{
console.log(req.body);
var bitmap = new Buffer(req.body.data, 'base64');
let dirpath = `${process.cwd()}/signals.txt`;
let signalPath = path.normalize(dirpath);
connection.connect();
fs.writeFile(signalPath, bitmap, async (err) => {
if (err) throw err;
console.log('Successfully updated the file data');
//all the ending brackets and stuff
Now the thing is the orignal file looks like this :
Time,1,2,3,4,5,6,7,8,9,10,11,12
0.000000,7.250553,14.951141,5.550423,2.850217,-1.050080,-3.050233,1.850141,2.850217,-3.150240,1.350103,-2.950225,1.150088
But the file when writing back from base64 looks like this :
u«Zµìmþ™ZŠvÚ±î¸Time,1,2,3,4,5,6,7,8,9,10,11,12
0.000000,1.250095,0.250019,-4.150317,-0.350027,3.650278,1.950149,0.950072,-1.250095,-1.150088,-7.750591,-1.850141,-0.050004
See the weird characters in the beginning ? Why is this happening.
Remember to read up on what the functions you use do, because you're using readAsDataURL which does not give you the base64 encoded version of your data: it gives you Data-URL, and Data-URLs have a header prefix to tell URL parsers what kind of data this will be, and how to decode the data directly following the header.
To quote the MDN article:
Note: The blob's result cannot be directly decoded as Base64 without first removing the Data-URL declaration preceding the Base64-encoded data. To retrieve only the Base64 encoded string, first remove data:*/*;base64, from the result.
If you don't, blindly converting the Data-URL from base64 to plain text will give you some nonsense data at the start:
> Buffer.from('data:*/*;base64', 'base64').toString('utf-8')
'u�Z���{�'
Which raises another point: you would have caught this with POST data validation, because the Data-URL that you sent contains characters that are not allowed in base64. POST validation is always a good idea.
I know this isn't the exact code, but it is difficult to reproduce your problem with the code you provided. But the data you are sending needs to be a URL/URI encoded form.
So essentially:
encodeURI(base64data);
Encode URI is built into javascript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI
EDIT:
I saw you used the function readDataAsUrl(), but try using the encodeURI function and then readDataAsUrl().

How to render utf8 encoded blob in VueJS/Javascript

I am trying to convert a binary blob which is fetched from database into an image which I can render with an <img>-Tag.
In my Laravel backend I return the blob/data like this:
$user = User::find($id);
$user->photo = utf8_encode($user->photo); // $user->photo is saved as blob in MySQL database
return $user;
The reason why I use utf8_encode is that the data is returned as JSON.
In VueJS I did try the following to render the image:
<input type="file" #change="onFileSelect">
<img :src="setImage(userdata.photo)">
setImage(image) {
let objurl = window.URL.createObjectURL(new Blob([image]));
return objurl;
}
onFileSelect(e) {
this.userdata.photo = e.target.files[0];
}
If I select a file in input the image is rendered accordingly. But when I fetch my data from server it should load the saved image.
If I console.log(image) inside the setImage(image) method after the server request I get the following output:
PNG
IHDRu<ÔFiCCPICC ProfileHWXSÉ[RIh¤ÞD)Ò¥Ð"Hl$PBL"vdQÁµØÐUE×ÈZ±E±÷e],ØPyºú½÷¾w¾ïÜûçÌÿ̽w­®Dj+ÎÆ3Ç¥¤2I0t.O&aÅÆF(÷Ê»ëQܯ8+¸~ÿ¯¢ÃÈx ±§óe¼\÷ð$Ò|ÞÐn55_¢À ÖÂ!(p¦
(pº
W*}âØïLãr¥h6C;³ y4oBì"æÄh!à ¹|# §ÀÐاÇùÎôAN.7s«jQ
9D$äp§ýíøß#a&FÄ)j}»©À4»ÅéÑ1ëBüAÄWúCRòD?j±aÏÄ.|nH$Ä&s¢£ÔöôQb¸BÐBQ>'A=w#¯æ¬æÅÅà)¥ÛÀãüOȳYjþBgÿm0!Y3F-%EC¬ ±,;>RåY ÙÑ>Ry"k}âð?6)C§öæÊêÅEh5®Ê&D¨yvð¸Êü
!nY<Ù¸¨ZøPUíØ%8Q]/Ö!ÉSÏ}-ÉUûãTAN¸Ân ±¬ ^=ÈRÅGKòcTyâéYÜѱª|ðBØ 0j:ÈY#ÔÖÝÔ
©FÂHA&gµeF²rD¯ñ üÈç+G Ú¿ZUWg¡-PÎÈO Î þ+g£%ÇÐ"ú):æU1ö³-Qj|©5àI%#aDÜÀýð(x
ê{ã>Ù~ó'bG°fìvHWÑcå*§Ì'ò~ÇUÇTtRæRïÒåòY5/(T¼;O2M*Êæ3YðÍ/rļáÃn.®ð­­ø¨^S=ßÄHçm|nGi÷÷÷øf#ãP¨ÝßlöЯpf>O.-PÙpÅ¿PZð2fÀ
If I pass that stuff to my setImage() method I do not get any error. The blob URL is created but the <img> Tag just shows an "empty" image.
And so on. Is there a way how I can convert this utf8_encoded blob into something useful?
I cannot comment, so had to post as an answer. I will remove if needed.
You could use a library to handle blob parsing on the front-end.
https://www.npmjs.com/package/blob-util
That library has built in ES module support, so should work out-of-the-box with Vue.

Upload file as JSON to Python webserver

I want to upload a file as JSON from the client to a Python webserver (Tornado) and save it on the server. This is my simplified setup:
Client HTML:
<input type="file" id="myFile" onchange="fileChange" />
Client JS:
function fileChange(event) {
const file = event.target.files[0];
const fileReader = new FileReader();
fileReader.onload = (e) => uploadFile(e.target.result, file.name);
fileReader.readAsText(file);
}
function uploadFile(fileContent, fileName) {
const data = {fileContent, fileName};
axios.post('http://localhost:8080/api/uploadFile', JSON.srtingify(data));
}
Python Webserver:
class UploadFileHandler(tornado.web.RequestHandler):
def post(self):
requestBody = tornado.escape.json_decode(self.request.body)
file = open(requestBody["fileName"], "w+")
file.write(requestBody["fileContent"].encode("UTF-8"))
file.close()
All uploaded files are empty (blank pages in a PDF, file type of JPG is 'not supported', Word file cannot be opened) and are nearly twice as big as the original file. How can I fix this?
Is there a way to improve this setup?
You are trying to upload binary files (word, jpg), serialised as JSON, and store them on the server.
To handle binary data in JSON, encode the binary data as base64 first, then call JSON.stringify.
Like this (untested):
function uploadFile(fileContent, fileName) {
// Encode the binary data to as base64.
const data = {
fileContent: btoa(fileContent),
fileName: fileName
};
axios.post('http://localhost:8080/api/uploadFile', JSON.stringify(data));
}
On the server side, you need to deserialise from JSON, decode the base64 and the open a file in binary mode to ensure that what you are writing to disk is the uploaded binary data. Opening the file in text mode requires that the data be encoded before writing to disk, and this encoding step will corrupt binary data.
Something like this ought to work:
class UploadFileHandler(tornado.web.RequestHandler):
def post(self):
requestBody = tornado.escape.json_decode(self.request.body)
# Decode binary content from base64
binary_data = base64.b64decode(requestBody[fileContent])
# Open file in binary mode
with open(requestBody["fileName"], "wb") as f:
f.write(binary_data)

Categories