Skip to content
This repository has been archived by the owner on Apr 12, 2022. It is now read-only.

Images in Example project as Couchbase Lite attachments #11

Closed
jamesnocentini opened this issue Jan 13, 2016 · 45 comments
Closed

Images in Example project as Couchbase Lite attachments #11

jamesnocentini opened this issue Jan 13, 2016 · 45 comments

Comments

@jamesnocentini
Copy link
Contributor

Test that images can be inserted as attachments and replicated accordingly. /cc @ssomnoremac

@ssomnoremac
Copy link

@jamiltz it seems that you are right, it works to use the attachment as a uri like so

<Image 
          source={{uri: "http://localhost:5984/myapp/100/pic100.jpg"}} 
          style={styles.thumbnail} />

that's great! But when I looked at the device database it hadn't replicated the attachment from sync_gateway so that's why it's not showing up. Let me double check and restart sync and try again

@jamesnocentini
Copy link
Contributor Author

👍 Ok let me know if the replication works

@ssomnoremac
Copy link

Nevermind, it's there. Still not working though so it's something else.

@jamesnocentini
Copy link
Contributor Author

What is there? And what is not working just to be clear on what I should run.

@ssomnoremac
Copy link

so on my forwarded port i can see this
http://localhost:5984/myapp/100/pic100.jpg
is definitely there. However, the above Image component is not working

@jamesnocentini
Copy link
Contributor Author

Ah ok that's a pity. Can you push the code on a branch on your fork?

@ssomnoremac
Copy link

definitely, I'll try and do that soon. but I did a curl to get the img up so it will be hard for you to replicate my exact sync_gateway. You'll have to curl an attachment in yourself.

@ssomnoremac
Copy link

yeah, so it's just one line

and to curl in an image something like this (but you'll have to add a document with id of 100)

curl -v -X PUT http://127.0.0.1:4984/moviesapp/100/pic100.jpg?rev=[rev # here] --data-binary @pic100.jpg -H "Content-Type: image/jpg"

@jamesnocentini
Copy link
Contributor Author

Thanks that's great I'll have a look tomorrow.

@ssomnoremac
Copy link

I've done a little testing

  • using a cloudant url https FAILED
  • using a couchdb url http WORKED
  • using localhost:5984 http FAILED

So perhaps CouchbaseLite is adding a https-like security layer? Can the response headers for couchbase lite be modified? CouchDB is specifying content type. Perhaps that is needed.

@ssomnoremac
Copy link

@jamiltz almost certain this is a CORS issue on the lite server. Can you advise how to allow *

@jamesnocentini
Copy link
Contributor Author

@ssomnoremac sorry for the lack of response. Could be! What makes you think CORS? I've was biten so many times by it ;) Have you checked the request headers with httpscoop or charles?

@ssomnoremac
Copy link

@jamiltz I couldn't figure out how to sniff the http request between my app and the the cbLite server using Charles. I'm not too familiar with Maven but, assuming this is CORS, making this kind of modification would require forking the couchbase maven repo or pulling the entire cb code into the app, correct?

@louisruch
Copy link

@jamiltz, @ssomnoremac So the issue and reason we think it's a CORS issue is firstly as you mentioned this has bitten us before :) We had basically the exact same problem when developing our Angular app on CouchDB. From the browser in chrome we are able to get access to the attachment using the same URI as in the react native app. Now the reason CORS exists is to control exactly what we are trying to do - take an asset from service x and re serve it.

Looking at line 60 of the LiteServlet I see the only header being added is www-auth, ideally we would add origin here to remove our expected problem. The perfect way would be to pass all the headers we want similarly to how credentials is done!

https://github.com/couchbase/couchbase-lite-java-listener/blob/release/1.1.0/src/main/java/com/couchbase/lite/listener/LiteServlet.java

I have played around with this a bit and cannot seem to find any other reason other than CORS. Changing our code to request the URI from our couchDB AWS hosted server presents the image perfectly.

@jamesnocentini
Copy link
Contributor Author

I finally got down to reproducing the issue and it turns out to be the Image component stripping out the credentials from the url to which CBL Listener returns a 401 Unauthorized. So http://admin:pass@localhost... becomes http://localhost....

I see two possible workarounds:

  • Removing the credentials on the Listener which may not be desirable in production.

  • Get the attachment as base 64 encoded. If the doc id is 1 then the request will be:

    $ curl 'http://localhost:5984/myapp/1?attachments=true'
    
    {"_attachments":{"photo":{"digest":"sha1-jm0YDvxvrWnYlesBkw9JKXuIi84=","revpos":2,"length":36718,"content_type":"image/png","data":"/9j/4AAQSkZJRgABAQA..."}}, "_id": 1, "_rev": "1-..."}
    

    The Image component accepts base64 encoded image. Prepend the photo.data property with data:image/png;base64, and it should work.

N.B: I recommend using Charles for network debugging the CBL REST API on a device or emulator. Start Charles then from the Android WiFi page, select the current network and add a manual proxy in advanced settings (set the hostname to the IP of your computer and port to 8888 - the default reverse proxy port on Charles). Return to the app and Charles should pick up the traffic going to the CBL Listener.

@louisruch
Copy link

Hey thanks,

Yeah was going to try base64 today anyway to see if that works and I can confirm it does - I think we will use this as a work around! Thanks @jamiltz

@npomfret
Copy link
Contributor

Does anyone have a working example of how to PUT and image attachment to CBL using the rest API?

@jamesnocentini
Copy link
Contributor Author

@npomfret did you try the workaround to base 64 encode the attachment? (see @SolmyrBhaal's implementation for reference https://github.com/InfiniteLibrary/infinite-reader/pull/16/files)

@npomfret
Copy link
Contributor

npomfret commented Apr 20, 2016

Thanks for the reply. I'm not quite sure what I'm seeing there. Is his workaround to just attach the raw base64 data instead of binary encoding it. Basically just like attaching a text file? I've not tried that but I'm sure it would work.

I wanted to shrink the attachment size down though. I understand base64 encoding of images is quite bloated.

There is a way to do it but I can't get the libraries to work in RN

@jamesnocentini
Copy link
Contributor Author

Yes you can add attachments by encoding them in base64 in the body. You can check the documentation or the code in comments above. Ideally, we could just pass a url to the Image component but as explained above, React Native strips out the auth header (need to check if it's still the case with React Native 0.23). In any case, I think we should update the ReactNativeCouchbaseLiteExample app to use the attachments for the movie thumbnails.

@jamesnocentini
Copy link
Contributor Author

jamesnocentini commented Apr 21, 2016

No update in RN 0.23 regarding this issue. Adding images via the PUT /dbname/docname/attachmentname?rev=... endpoint and retrieving them as base64 with attachments=true is the workaround. @SolmyrBhaal / @ssomnoremac did you use base64 attachments on iOS and/or Android. I found it works on iOS but the attachments=true option on Android doesn't return the attachment inlined in the json response body.

@louisruch
Copy link

@jamiltz we are using Android. There are two problems:

  1. Get
    The fetch is stripping the credentials in the http get call to get the attachment so one solution is to leave the credentials blank - no user name or password when running init:
    ReactCBLite.init(5984, '', '', (e) => {});
    The other option is the base64 example where you request the via the CBLite Manager and get the document with the base64 inline in the JSON:
    return catalogDB.getDesignDocument('_all_docs?include_docs=true&attachments=true')}

  2. Put
    So the other issue we had was putting the binary data, we did resolve it as mentioned above by just including a plain/txt document which stored the encrypted base64 data - the problem here is it does give a 25-40 percent increase in the file size. The problem we had which has not been resolved was that the fetch being returned kept throwing an error when downloading and trying to upload again. We tried a number of different methods in the first then((res... blob, text, etc nothing worked - eventually we settled on the plain/txt version to get something out...

fetch(page.fullPath) .then((res) => res.text()) // Problem here .then((img) => { console.log("downloading page number ", index) if (rev === undefined) { return booksDB.createAttachment(page.docName, img, page.attachmentName, 'plain/txt') } else { return booksDB.createAttachment(page.docName, img, page.attachmentName, 'plain/txt', rev) } }) .then((res) => { dispatch(pageDownloadComplete(imgArray)) return dispatch(asyncDownloadPage(imgArray, index, res.rev)) }) .catch((err) => { throw err; })

@npomfret
Copy link
Contributor

I think I've got something working that doesn't do the base64 attachment upload. I've had to use a package called react-native-fileupload which I understand will upload the binary data rather than the bloated base64 data. My work so far is on this fork.

The problem I've got at the moment is my getAttachment function returns a Blob. Not sure how to turn that into an image or even a file on disk yet.

@louisruch
Copy link

Does this plugin have support for the path variable to be another URL or
must you save the attachment locally on the device file system before you
can upload?

On Sunday, April 24, 2016, Nick Pomfret notifications@github.com wrote:

I think I've got something working that doesn't do the base64 attachment
upload. I've had to use a package called react-native-fileupload which I
understand will upload the binary data rather than the bloated base64 data.
My work so far is on this fork
https://github.com/npomfret/react-native-couchbase-lite/blob/master/index.js
.

The problem I've got at the moment is my getAttachment function returns
a Blob. Not sure how to turn that into an image or even a file on disk yet.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#11 (comment)

Kind Regards,

Louis C. Ruch

@npomfret
Copy link
Contributor

npomfret commented Apr 24, 2016

Not tried a remote url yet. I'm just currently stuck on getting it back out of the BCL database

@louisruch
Copy link

I'm not at my machine to test, but it should work fine as a blob, if the
attachment type is set as 'image/jpg' or whatever it should be, png, etc

Have you tried forwarding port 5984 and accessing cblite from your machine
to test?

'adb forward tcp:5984 tcp:5984'

Then from your machine just access the cblite database via browser

http://127.0.0.1:5984/dbname/docname/attachment

That way you can at least download and test the attachment and see if
everything is good in a controlled environment...

On Sunday, April 24, 2016, Nick Pomfret notifications@github.com wrote:

Yes it works with a url. I'm just currently stuck on getting it back out
of the BCL database


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#11 (comment)

Kind Regards,

Louis C. Ruch

@npomfret
Copy link
Contributor

I may have spoken to soon. The upload is being accepted, but I think its not expecting a multipart form data type body, rather a stream of bytes. So the data that gets saved into CBL contains a load of http related text as well as the binary data. Need to find a way to post an http body with raw binary data in it.

@npomfret
Copy link
Contributor

npomfret commented Apr 24, 2016

Ok, I've finally got the PUT with binary data working. I've to write some native code to stream the binary data across into CBL.

The java code, is a work in progress but it does work. The base64 workaround is still needed to view the file, but that's fine. It was the increased network overhead I was worried about.

And this implementation will take a file path or a URL as the source, so the source binary data doesn't need to be one the device.

I've never written any IOS code so this really needs another set of eyes.

@npomfret
Copy link
Contributor

Has anyone managed to get a document with its attachments in android? My response is missing the crucial data part:

_attachments: 
  { 'fe4f4891-d90d-4fc9-9051-0541f5a9c6a7': 
    { digest: 'sha1-g9aU9ZF5SoSQw7BYXH/f5JBuO4A=',
      length: 1572,
      revpos: 2,
      stub: true,
      content_type: 'image/jpeg' } }

@npomfret
Copy link
Contributor

The bug of the stripped auth text in the url is android only. I've raised an issue but I can't find the problem, so can't fix it... yet.

However, the binary upload works in both IOS and Android now. And the attachment URL works nicely in IOS.

@jamesnocentini
Copy link
Contributor Author

Re attachments=true I think it might be because that querystring option isn't handled by the Android Listener. I've opened it at https://github.com/couchbase/couchbase-lite-java-core/issues/1210.

@npomfret
Copy link
Contributor

Ok, thanks. So an android we're a bit stuck because we can't get attachments out either way.

If we can get the Facebook Fresco project updated so that Android can load images with auth in the urls the same way as IOS I think we're good. I'll tidy up the binary upload stuff a bit in my fork and submit it. I could really do with some help with the Objective C stuff. Especially around error handling and waiting for the upload to complete.

@npomfret
Copy link
Contributor

I've written a bit of native code to deal with upload binary attachments. Not added instructions to the docs yet but it would be good if someone else could try it out

#42

New function is called saveAttachment, and to load the data use getAttachmentUri to generate a url. As discussed - the urls don't work for images in android yet.

@jamesnocentini
Copy link
Contributor Author

Thanks! I'll give this a go. I'm curious why we need the saveAttachment method, isn't the PUT /dbname/docid/attachmentname endpoint suited for that? To be honest, I've only tried to add attachment using Postman and the nodejs request module. But I imagine that it should be possible with FormData/XmlHttpRequest/fetch.

@npomfret
Copy link
Contributor

npomfret commented Apr 26, 2016

Hi James. Yes, its not obvious, (or I've got something very wrong!). What I found was that CBL would accept a From posted / multipart form data upload, but when I read it back the content wasn't what I expected. Both XmlHttpRequest and fetch will send a multipart form post with the file in it.

When I looked in to the code I saw that CBL is expecting the body to contain only binary data. CBL doesn't parse out the multipart form data stuff (the boundary info and blank lines), and so what gets saved to disk is your binary data + some unwanted http stuff. Which obviously come back out again when you query it.

I couldn't find another way to send binary data across using java script so...

The native method I've added here, which still need a little work but seems to do the job, just streams the contents of a url into CBL with no additional http stuff like what you get with a multipart form post. The source of the url doesn't even need to be a file on your phone.

I've tested on both devices. IOS work fine. But in Android, although the upload looks fine, as we've discussed there's an issue with RN where it doesn't add the auth headers in android so you get a 401 response when trying to download the attachment. I've tried unsuccessfully to get this fixed. Not sure what to do about it now.

Maybe add (temporarily) another native method to take an attachment and just put it on disk somewhere. Until the issue is fixed properly.

Does any of that make sense?

@npomfret
Copy link
Contributor

I've made some updates to my PR, now #43

So it handles IOS asset type URIs (of the sort you get back from the camera roll.

If anyone has had a chance to try it out please let me know - it would be good to get merged.

@ssomnoremac
Copy link

@npomfret , saw all your work on this just now. I've been out of the country. Sorry I couldn't help out but it looks like you made good progress. I will definitely try this out soon.

Will your work offer a solution to core RN? This is where I went looking to find a solution.

@npomfret
Copy link
Contributor

Thanks!

What do you mean about "Will your work offer a solution to core RN?"

@ssomnoremac
Copy link

Doesn't your solution (Java Code) solve the problem posed by the thread I referenced: "Does fetch with blob() marshal data across the bridge". Are you not now marshaling the binary data across the bridge?

@npomfret
Copy link
Contributor

No, all the native code does (both IOS and android) is take a uri and stream the binary data from it to another uri as the body of a PUT or POST.

@jamesnocentini
Copy link
Contributor Author

@npomfret It looks like java-core just got ?attachments=true support (https://github.com/couchbase/couchbase-lite-java-core/issues/1210) so once we've merged #43 we can try it. The latest nightly build of cbl android should have this change (http://latestbuilds.hq.couchbase.com/couchbase-lite-android/0.0.0/)

@npomfret
Copy link
Contributor

npomfret commented May 3, 2016

Ok, i'll try and get it merged today.

@npomfret
Copy link
Contributor

I'm going to close this ticket as I believe the new release copes with binary attachments of any sort.

@npomfret
Copy link
Contributor

npomfret commented Jun 8, 2016

Looks like the Android OkHttp library that react-native uses on Android doesn't support urls containing basic auth parameters and so images coming from the CBL database won't be displayed.

square/okhttp#2143

There was talk of them fixing it in OkHttp. As of the latest RN 0.27 (which I believe got a new version of OkHttp) it is still not working.

@bjornd
Copy link

bjornd commented May 19, 2018

For anyone having problems with attachments in react-native-couchbase-lite I would like to suggest to try a new React Native module for Couchbase Lite react-native-cbl. It implements all the methods via native code calls providing better performance and richer feature set.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants