2

I'm using couchdb to store attachments that I need to display in the browser.

The data is uploaded from an html input and then processed when saveDoc is called:

getFileData: function(file){
    var reader = new FileReader();
    return new Promise(function(accept, reject){
        reader.onload = (e) => {
            accept(e.target.result)
        };
        reader.readAsDataURL(file);
    })
},
saveDoc: function(name, type, filedata, url){
    console.log(filedata)
    var self=this
    return new Promise(function(accept, reject){
        self.getData(url).then(data => {
            var rev = data['_rev']
            console.log(url + ' is the url')
            console.log(name + ' is the filename')
            documentation.attachment.insert(url, name, filedata, type,
                                            { rev: rev }, function(err, body) {

                                                if (!err){
                                                    console.log(body);
                                                }
                                                else {
                                                    console.log(err)
                                                }
                                            })
        }).catch(err => {
            console.log(err)
        })
    })
},

I don't get any errors while uploading from the console. But when I navigate to where the attachment should be in the console, I see a browser message telling me the data can't be displayed (for pdf/images), or I see a base64 string that looks like this:

data:image/png;base64,iVBOR...

when the attachment is an html document.

(The data being logged on saveDoc looks like this: data:application/pdf;base64,JVBER...)

The correct content type as well as a reasonable length is being displayed in my couchdb admin with metadata on the files, so there are no obvious header problems. Can anyone think of any other reason this might not be working in the browser?

Edit

To give some more detail, I uploaded a pdf in Fauxton, which works as expected and displays in teh browser. I then uploaded the same pdf using my saveDoc function, and it somehow added a huge amount of data to the length of the document.

version uploaded in Fauxton:

  "_attachments": {
    "03_IKB-RH_FUB_mitDB.pdf": {
      "content_type": "application/pdf",
      "revpos": 5,
      "digest": "md5-tX7qKPT6b7Ek90GbIq9q8A==",
      "length": 462154,
      "stub": true
    }
  }

version uploaded programmatically:

  "_attachments": {
    "03_IKB-RH_FUB_mitDB.pdf": {
      "content_type": "application/pdf",
      "revpos": 4,
      "digest": "md5-Zy8zcwHmXsfwtleJNV5xHw==",
      "length": 616208,
      "stub": true
    }
  }
David J.
  • 1,753
  • 13
  • 47
  • 96
  • What are you trying to achieve there? Render the file in some web page? Download the file? Decode it from base64? – HRK44 Jan 29 '18 at 13:46
  • When I upload an html page and navigate to it, I want to see the html content rendered as html in the browser. Instead I see text with the base64 code (or nothing, if it's a pdf or image) – David J. Jan 29 '18 at 14:11
  • Is the base64 encoding a requirement from you in this case? Or does it just happen to be encoded in base64? Basically you can check the type of the file you are uploading via the FileReader/Uploader, then if it's an HTML page, you can save it as plain text and if it's an image or pdf you can save it as base64. To render the base64, you will need an HTML tag as or some PDF viewer libraries. – HRK44 Jan 29 '18 at 14:15
  • @HRK44 The base64 is the result of the reader using the `readAsDataURL` method. I'm encoding it this way so I can upload it to Couchdb from the user's input. – David J. Jan 29 '18 at 14:19
  • 1
    Well in this case (if the data is correctly encoded in your DB), you will need to setup your server so it decodes the base64 back into html content, then send it to the client via setting server response as 'Content-Type: text/html;'. Also you might want to check this link : https://stackoverflow.com/questions/9238890/convert-html-to-datatext-html-link-using-javascript – HRK44 Jan 29 '18 at 14:55
  • Are you facing the problem, while rendering [`base64` string](https://stackoverflow.com/a/34544807/5081877) in HTML element as src. or facing problem in uploading the file. Instead of a base64 string, you can use `nodejs « new Busboy({ headers : req.headers }); as a file stream` . – Yash Jan 30 '18 at 10:06
  • @Yash Apparently I'm having a problem with uploading the file because the data couch is expecting isn't what I'm sending. Please see edit. – David J. Feb 02 '18 at 14:31
  • @HRK44 I should have checked this before, but it seems that this is an issue with using the file reader api to get the base64 data. Somehow the data I'm sending couch differs in length from what couch is expecting. Please see edit. – David J. Feb 02 '18 at 14:33

1 Answers1

0

The data property of a particular ._attachments{} member should be base64, not dataURL.

When you convert your file to dataURL, you do get base64-encoded binary, but with special prefix. Truncate the prefix, and save only base64-encoded tail.

Basically, to obtain valid base64 you should remove data:*/*;base64, prefix from the beginning of a dataURL-encoded string.

UPD. After diving deeper it turned out, that .attachment.insert(url, name, filedata, type) method accepts binary (not base64) as filedata. To obtain binary from FileReader, fr.readAsArrayBuffer() should be used instead of fr.readAsDataURL().

ermouth
  • 835
  • 7
  • 12
  • Tried your suggestion and removed the prefix, but I got the exact same result. – David J. Feb 02 '18 at 14:13
  • I edited the question to explain how the uploaded data differs from what couch is expecting. – David J. Feb 02 '18 at 14:30
  • 1
    It’s very likely you need not encode data at all. Seems that driver you use (something like nano, I guess), re-encodes your already encoded base64 again. At least size increase is exactly +1/3, which is a good marker of such encoding. Try to pass ArrayBuffer as filedata, it might help. To obtain ArrayBuffer, use readAsArrayBuffer() instead of readAsDataURL(). – ermouth Feb 02 '18 at 17:18
  • readAsArrayBuffer() turned out to be the answer. I also needed to turn the result into a Uint8Array using `new Uint8Array(e.result.target)`. I'm giving you the points and I'll accept your answer if you update it to include the readAsArrayBuffer suggestion. Thanks for the help. – David J. Feb 05 '18 at 07:46