/// <summary> /// Given a revision, read its _attachments dictionary (if any), convert each attachment to a /// AttachmentInternal object, and return a dictionary mapping names->CBL_Attachments. /// </summary> /// <remarks> /// Given a revision, read its _attachments dictionary (if any), convert each attachment to a /// AttachmentInternal object, and return a dictionary mapping names->CBL_Attachments. /// </remarks> /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> internal IDictionary<String, AttachmentInternal> GetAttachmentsFromRevision(RevisionInternal rev) { var revAttachments = rev.GetPropertyForKey("_attachments").AsDictionary<string, object>(); if (revAttachments == null || revAttachments.Count == 0 || rev.IsDeleted()) { return new Dictionary<string, AttachmentInternal>(); } var attachments = new Dictionary<string, AttachmentInternal>(); foreach (var name in revAttachments.Keys) { var attachInfo = revAttachments.Get(name).AsDictionary<string, object>(); var contentType = (string)attachInfo.Get("content_type"); var attachment = new AttachmentInternal(name, contentType); var newContentBase64 = (string)attachInfo.Get("data"); if (newContentBase64 != null) { // If there's inline attachment data, decode and store it: byte[] newContents; try { newContents = StringUtils.ConvertFromUnpaddedBase64String (newContentBase64); } catch (IOException e) { throw new CouchbaseLiteException(e, StatusCode.BadEncoding); } attachment.SetLength(newContents.Length); var outBlobKey = new BlobKey(); var storedBlob = Attachments.StoreBlob(newContents, outBlobKey); attachment.SetBlobKey(outBlobKey); if (!storedBlob) { throw new CouchbaseLiteException(StatusCode.StatusAttachmentError); } } else { if (attachInfo.ContainsKey("follows") && ((bool)attachInfo.Get("follows"))) { // "follows" means the uploader provided the attachment in a separate MIME part. // This means it's already been registered in _pendingAttachmentsByDigest; // I just need to look it up by its "digest" property and install it into the store: InstallAttachment(attachment, attachInfo); } else { // This item is just a stub; validate and skip it if (((bool)attachInfo.Get("stub")) == false) { throw new CouchbaseLiteException("Expected this attachment to be a stub", StatusCode. BadAttachment); } var revPos = Convert.ToInt64(attachInfo.Get("revpos")); if (revPos <= 0) { throw new CouchbaseLiteException("Invalid revpos: " + revPos, StatusCode.BadAttachment); } continue; } } // Handle encoded attachment: string encodingStr = (string)attachInfo.Get("encoding"); if (encodingStr != null && encodingStr.Length > 0) { if (Runtime.EqualsIgnoreCase(encodingStr, "gzip")) { attachment.SetEncoding(AttachmentEncoding.AttachmentEncodingGZIP ); } else { throw new CouchbaseLiteException("Unnkown encoding: " + encodingStr, StatusCode.BadEncoding ); } attachment.SetEncodedLength(attachment.GetLength()); if (attachInfo.ContainsKey("length")) { attachment.SetLength((long)attachInfo.Get("length")); } } if (attachInfo.ContainsKey("revpos")) { var revpos = Convert.ToInt32(attachInfo.Get("revpos")); attachment.SetRevpos(revpos); } attachments[name] = attachment; } return attachments; }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> internal void InstallAttachment(AttachmentInternal attachment, IDictionary<String, Object> attachInfo) { var digest = (string)attachInfo.Get("digest"); if (digest == null) { throw new CouchbaseLiteException(StatusCode.BadAttachment); } if (PendingAttachmentsByDigest != null && PendingAttachmentsByDigest.ContainsKey(digest)) { var writer = PendingAttachmentsByDigest.Get(digest); try { var blobStoreWriter = writer; blobStoreWriter.Install(); attachment.SetBlobKey(blobStoreWriter.GetBlobKey()); attachment.SetLength(blobStoreWriter.GetLength()); } catch (Exception e) { throw new CouchbaseLiteException(e, StatusCode.StatusAttachmentError); } } }