Reputation: 891
I'm storing a file (either a PDF or PNG) as a binary string in indexedDB and retrieving it later.
For reasons unknown to me, the downloaded files are slightly different from the original files. I am unable to open the downloaded files using appropriate software (i.e. PDF reader), while the original file opens as expected.
When storing plaintext files, the files are exactly identical when checked with diff
or cmp
.
I noticed that there were a couple of differences when viewing the binary files in a hex editor, as shown below.
According to cmp
, these are the only differences.
Original:
00000000: 2550 4446 2d31 2e35 0a25 d0d4 c5d8 0a31 %PDF-1.5.%.....1
00000010: 3533 2030 206f 626a 0a3c 3c0a 2f4c 656e 53 0 obj.<<./Len
Downloaded:
00000000: 2550 4446 2d31 2e35 0a25 c390 c394 c385 %PDF-1.5.%......
00000010: c398 0a31 3533 2030 206f 626a 0a3c 3c0a ...153 0 obj.<<.
I also tried storing and retrieving the files encoded as base64 using atob() and btoa(), however, this produced the same result. There was no difference between the file saved as a binary string and the file saved as a base64 string.
Source code:
let db;
let dbVersion = 1;
let dbReady = false;
function initDb() {
let request = indexedDB.open('FileStorage', dbVersion);
request.onerror = function(e) {
console.error('Unable to open database.');
}
request.onsuccess = function(e) {
db = e.target.result;
console.log('db opened');
}
request.onupgradeneeded = function(e) {
let db = e.target.result;
db.createObjectStore('files', {keyPath:'id', autoIncrement: false});
dbReady = true;
}
}
function uploadFile(fileId) {
fileIn = document.getElementById('fileUpload');
if(fileIn.files && fileIn.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
console.log(e.target.result);
let bits = e.target.result;
let ob = {
id: fileId,
type: fileIn.files[0].type,
name: fileIn.files[0].name,
data: bits
};
let trans = db.transaction(['files'], 'readwrite');
let addReq = trans.objectStore('files').put(ob);
addReq.onerror = function(e) {
console.log('error storing data');
console.error(e);
}
trans.oncomplete = function(e) {
console.log('data stored');
}
};
reader.readAsBinaryString(fileIn.files[0])
}
}
function downloadFile(fileId) {
console.log('downloading');
var trans = db.transaction(['files'], 'readonly');
var dlReq = trans.objectStore('files').get(fileId);
dlReq.onerror = function(e) {
console.log('error reading data');
console.error(e);
};
dlReq.onsuccess = function(e) {
console.log('data read');
console.log(dlReq.result);
var element = document.createElement('a');
element.setAttribute('href', 'data:' + dlReq.result.type + ';charset=utf-8,' + encodeURIComponent(dlReq.result.data));
element.setAttribute('download', dlReq.result.name);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
};
}
<!DOCTYPE html>
<html>
<head>
<meta charset = "UTF-8"/>
</head>
<body>
<section>
<input id="fileUpload" name="fileUpload" type="file"/>
<button id="upload" type="button" onclick="uploadFile('test');">Go</button>
<button id="download" type="button" onclick="downloadFile('test');">Download</button>
</section>
</body>
</html>
I would love to know the reason for these differences. Also, any help with potential solutions or workarounds would be greatly appreciated.
Upvotes: 4
Views: 2719
Reputation: 891
I compared the stored base64 file with the original file encoded as base64. There were no differences between these files, so it was likely to do with how the file was encoded before being downloaded.
element.setAttribute('href', 'data:' + dlReq.result.type + ';charset=utf-8,' + encodeURIComponent(atob(dlReq.result.data)));
It is possible that charset
was set to the wrong value to properly encode the file, though I'm not sure about this.
My workaround was to change this line to download the file from base64 directly, rather than decoding it and encoding the URI component.
element.setAttribute('href', 'data:' + dlReq.result.type + ';base64,' + dlReq.result.data);
The file produced by this method was downloaded and opened without issue.
Upvotes: 4