1212import com .couchbase .lite .Status ;
1313import com .couchbase .lite .internal .InterfaceAudience ;
1414import com .couchbase .lite .internal .RevisionInternal ;
15+ import com .couchbase .lite .support .BlobContentBody ;
1516import com .couchbase .lite .support .CustomFuture ;
1617import com .couchbase .lite .support .HttpClientFactory ;
1718import com .couchbase .lite .support .RemoteRequest ;
2122import com .couchbase .lite .util .Log ;
2223import com .couchbase .lite .util .Utils ;
2324import com .couchbase .org .apache .http .entity .mime .MultipartEntity ;
24- import com .couchbase .org .apache .http .entity .mime .content .InputStreamBody ;
2525import com .couchbase .org .apache .http .entity .mime .content .StringBody ;
2626
2727import org .apache .http .HttpResponse ;
2828import org .apache .http .client .HttpResponseException ;
2929
3030import java .io .IOException ;
31- import java .io .InputStream ;
3231import java .net .URL ;
3332import java .nio .charset .Charset ;
3433import java .util .ArrayList ;
@@ -516,39 +515,33 @@ public void onCompletion(HttpResponse httpResponse, Object result, Throwable e)
516515 */
517516 @ InterfaceAudience .Private
518517 private boolean uploadMultipartRevision (final RevisionInternal revision ) {
519-
520- // holds inputStream for blob to close after using
521- final List <InputStream > streamList = new ArrayList <InputStream >();
522-
523518 MultipartEntity multiPart = null ;
524-
525519 Map <String , Object > revProps = revision .getProperties ();
526-
527520 Map <String , Object > attachments = (Map <String , Object >) revProps .get ("_attachments" );
528521 for (String attachmentKey : attachments .keySet ()) {
529522 Map <String , Object > attachment = (Map <String , Object >) attachments .get (attachmentKey );
530523 if (attachment .containsKey ("follows" )) {
531-
532524 if (multiPart == null ) {
533-
534525 multiPart = new MultipartEntity ();
535-
536526 try {
537527 String json = Manager .getObjectMapper ().writeValueAsString (revProps );
538528 Charset utf8charset = Charset .forName ("UTF-8" );
539529 byte [] uncompressed = json .getBytes (utf8charset );
540530 byte [] compressed = null ;
541531 byte [] data = uncompressed ;
542532 String contentEncoding = null ;
543- if (uncompressed .length > RemoteRequest .MIN_JSON_LENGTH_TO_COMPRESS && canSendCompressedRequests ()) {
533+ if (uncompressed .length > RemoteRequest .MIN_JSON_LENGTH_TO_COMPRESS &&
534+ canSendCompressedRequests ()) {
544535 compressed = Utils .compressByGzip (uncompressed );
545536 if (compressed .length < uncompressed .length ) {
546537 data = compressed ;
547538 contentEncoding = "gzip" ;
548539 }
549540 }
550- // NOTE: StringBody.contentEncoding default value is null. Setting null value to contentEncoding does not cause any impact.
551- multiPart .addPart ("param1" , new StringBody (data , "application/json" , utf8charset , contentEncoding ));
541+ // NOTE: StringBody.contentEncoding default value is null.
542+ // Setting null value to contentEncoding does not cause any impact.
543+ multiPart .addPart ("param1" , new StringBody (data , "application/json" ,
544+ utf8charset , contentEncoding ));
552545 } catch (IOException e ) {
553546 throw new IllegalArgumentException (e );
554547 }
@@ -557,88 +550,65 @@ private boolean uploadMultipartRevision(final RevisionInternal revision) {
557550 BlobStore blobStore = this .db .getAttachmentStore ();
558551 String base64Digest = (String ) attachment .get ("digest" );
559552 BlobKey blobKey = new BlobKey (base64Digest );
560- InputStream blobStream = blobStore .blobStreamForKey (blobKey );
561- if (blobStream == null ) {
562- Log .w (Log .TAG_SYNC , "Unable to load the blob stream for blobKey: %s - Skipping upload of multipart revision." , blobKey );
563- return false ;
564- } else {
565- streamList .add (blobStream );
566- String contentType = null ;
567- if (attachment .containsKey ("content_type" )) {
568- contentType = (String ) attachment .get ("content_type" );
569- } else if (attachment .containsKey ("type" )) {
570- contentType = (String ) attachment .get ("type" );
571- } else if (attachment .containsKey ("content-type" )) {
572- Log .w (Log .TAG_SYNC , "Found attachment that uses content-type" +
573- " field name instead of content_type (see couchbase-lite-android" +
574- " issue #80): %s" , attachment );
575- }
576-
577- // contentType = null causes Exception from FileBody of apache.
578- if (contentType == null )
579- contentType = "application/octet-stream" ; // default
580553
581- // NOTE: Content-Encoding might not be necessary to set. Apache FileBody does not set Content-Encoding.
582- // FileBody always return null for getContentEncoding(), and Content-Encoding header is not set in multipart
583- // CBL iOS: https://github.com/couchbase/couchbase-lite-ios/blob/feb7ff5eda1e80bd00e5eb19f1d46c793f7a1951/Source/CBL_Pusher.m#L449-L452
584- String contentEncoding = null ;
585- if (attachment .containsKey ("encoding" )) {
586- contentEncoding = (String ) attachment .get ("encoding" );
587- }
588-
589- InputStreamBody inputStreamBody =
590- new CustomStreamBody (blobStream , contentType ,
591- attachmentKey , contentEncoding );
592- multiPart .addPart (attachmentKey , inputStreamBody );
554+ String contentType = null ;
555+ if (attachment .containsKey ("content_type" )) {
556+ contentType = (String ) attachment .get ("content_type" );
557+ } else if (attachment .containsKey ("type" )) {
558+ contentType = (String ) attachment .get ("type" );
559+ } else if (attachment .containsKey ("content-type" )) {
560+ Log .w (Log .TAG_SYNC , "Found attachment that uses content-type" +
561+ " field name instead of content_type (see couchbase-lite-android" +
562+ " issue #80): %s" , attachment );
593563 }
564+ // contentType = null causes Exception from FileBody of apache.
565+ if (contentType == null )
566+ contentType = "application/octet-stream" ; // default
567+
568+ // CBL iOS: https://github.com/couchbase/couchbase-lite-ios/blob/feb7ff5eda1e80bd00e5eb19f1d46c793f7a1951/Source/CBL_Pusher.m#L449-L452
569+ String contentEncoding = null ;
570+ if (attachment .containsKey ("encoding" ))
571+ contentEncoding = (String ) attachment .get ("encoding" );
572+
573+ BlobContentBody contentBody = new BlobContentBody (blobStore , blobKey ,
574+ contentType , attachmentKey , contentEncoding );
575+ multiPart .addPart (attachmentKey , contentBody );
594576 }
595577 }
596-
597- if (multiPart == null ) {
578+ if (multiPart == null )
598579 return false ;
599- }
600-
601- final String path = String .format ("/%s?new_edits=false" , encodeDocumentId (revision .getDocID ()));
602580
603581 Log .d (Log .TAG_SYNC , "Uploading multipart request. Revision: %s" , revision );
604-
605582 addToChangesCount (1 );
606-
607- CustomFuture future = sendAsyncMultipartRequest ("PUT" , path , multiPart , new RemoteRequestCompletionBlock () {
608- @ Override
609- public void onCompletion (HttpResponse httpResponse , Object result , Throwable e ) {
610- try {
611- if (e != null ) {
612- if (e instanceof HttpResponseException ) {
613- // Server doesn't like multipart, eh? Fall back to JSON.
614- if (((HttpResponseException ) e ).getStatusCode () == 415 ) {
615- //status 415 = "bad_content_type"
616- dontSendMultipart = true ;
617- uploadJsonRevision (revision );
618- }
619- } else {
620- Log .e (Log .TAG_SYNC , "Exception uploading multipart request" , e );
621- setError (e );
622- }
623- } else {
624- Log .v (Log .TAG_SYNC , "Uploaded multipart request. Revision: %s" , revision );
625- removePending (revision );
626- }
627- } finally {
628- // close all inputStreams for Blob
629- for (InputStream stream : streamList ) {
583+ final String path = String .format ("/%s?new_edits=false" , encodeDocumentId (revision .getDocID ()));
584+ CustomFuture future = sendAsyncMultipartRequest ("PUT" , path , multiPart ,
585+ new RemoteRequestCompletionBlock () {
586+ @ Override
587+ public void onCompletion (HttpResponse httpResponse , Object result , Throwable e ) {
630588 try {
631- stream .close ();
632- } catch (IOException ioe ) {
589+ if (e != null ) {
590+ if (e instanceof HttpResponseException ) {
591+ // Server doesn't like multipart, eh? Fall back to JSON.
592+ if (((HttpResponseException ) e ).getStatusCode () == 415 ) {
593+ //status 415 = "bad_content_type"
594+ dontSendMultipart = true ;
595+ uploadJsonRevision (revision );
596+ }
597+ } else {
598+ Log .e (Log .TAG_SYNC , "Exception uploading multipart request" , e );
599+ setError (e );
600+ }
601+ } else {
602+ Log .v (Log .TAG_SYNC , "Uploaded multipart request. Revision: %s" , revision );
603+ removePending (revision );
604+ }
605+ } finally {
606+ addToCompletedChangesCount (1 );
633607 }
634608 }
635- addToCompletedChangesCount (1 );
636- }
637- }
638- });
609+ });
639610 future .setQueue (pendingFutures );
640611 pendingFutures .add (future );
641-
642612 return true ;
643613 }
644614
@@ -699,35 +669,6 @@ private static int findCommonAncestor(RevisionInternal rev, List<String> possibl
699669 return generation ;
700670 }
701671
702- // CustomFileBody to support contentEncoding. FileBody returns always null for getContentEncoding()
703- private static class CustomStreamBody extends InputStreamBody {
704- private String contentEncoding = null ;
705-
706- public CustomStreamBody (final InputStream in , final String mimeType ,
707- final String filename , String contentEncoding ) {
708- super (in , mimeType , filename );
709- this .contentEncoding = contentEncoding ;
710- }
711-
712- @ Override
713- protected void finalize () throws Throwable {
714- // close inputStream after used.
715- InputStream stream = getInputStream ();
716- if (stream != null ) {
717- try {
718- stream .close ();
719- } catch (IOException ioe ) {
720- }
721- }
722- super .finalize ();
723- }
724-
725- @ Override
726- public String getContentEncoding () {
727- return contentEncoding ;
728- }
729- }
730-
731672 /**
732673 * Submit revisions into inbox for changes from changesSince()
733674 */
0 commit comments