public InputStream GetContent() { if (body != null) { return(body); } else { Database db = revision.GetDatabase(); Couchbase.Lite.Attachment attachment = db.GetAttachmentForSequence(revision.GetSequence (), this.name); body = attachment.GetContent(); return(body); } }
/// <summary>Creates or updates an attachment.</summary> /// <remarks> /// Creates or updates an attachment. /// The attachment data will be written to the database when the revision is saved. /// </remarks> /// <param name="attachment">A newly-created Attachment (not yet associated with any revision)</param> /// <param name="name">The attachment name.</param> internal void AddAttachment(Attachment attachment, string name) { var attachments = Properties.Get("_attachments").AsDictionary<string,object>(); if (attachments == null) { attachments = new Dictionary<String, Object>(); } attachments[name] = attachment; Properties["_attachments"] = attachments; if (attachment != null) { attachment.Name = name; attachment.Revision = this; } }
internal static IDictionary <string, object> InstallAttachmentBodies(IDictionary <string , object> attachments, Database database) { IDictionary <string, object> updatedAttachments = new Dictionary <string, object>(); foreach (string name in attachments.Keys) { object value = attachments.Get(name); if (value is Couchbase.Lite.Attachment) { Couchbase.Lite.Attachment attachment = (Couchbase.Lite.Attachment)value; IDictionary <string, object> metadataMutable = new Dictionary <string, object>(); metadataMutable.PutAll(attachment.GetMetadata()); InputStream body = attachment.GetBodyIfNew(); if (body != null) { // Copy attachment body into the database's blob store: BlobStoreWriter writer = BlobStoreWriterForBody(body, database); metadataMutable.Put("length", (long)writer.GetLength()); metadataMutable.Put("digest", writer.MD5DigestString()); metadataMutable.Put("follows", true); database.RememberAttachmentWriter(writer); } updatedAttachments.Put(name, metadataMutable); } else { if (value is AttachmentInternal) { throw new ArgumentException("AttachmentInternal objects not expected here. Could indicate a bug" ); } else { if (value != null) { updatedAttachments.Put(name, value); } } } } return(updatedAttachments); }
/// <summary>Creates or updates an attachment.</summary> /// <remarks> /// Creates or updates an attachment. /// The attachment data will be written to the database when the revision is saved. /// </remarks> /// <param name="attachment">A newly-created Attachment (not yet associated with any revision)</param> /// <param name="name">The attachment name.</param> internal void AddAttachment(Attachment attachment, string name) { var attachments = Properties.Get("_attachments").AsDictionary<string, object>() ?? new Dictionary<string, object>(); var oldAttach = attachments.GetCast<Attachment>(name); if(oldAttach != null) { oldAttach.Dispose(); } if(attachment == null) { attachments.Remove(name); } else { attachments[name] = attachment; } Properties["_attachments"] = attachments; if(attachment != null) { attachment.Name = name; } }
public void TestGetAttachmentBodyUsingPrefetch() { // add a doc with an attachment var doc = database.CreateDocument(); var rev = doc.CreateRevision(); var properties = new Dictionary<string, object>(); properties["foo"] = "bar"; rev.SetUserProperties(properties); var attachBodyBytes = Encoding.UTF8.GetBytes("attach body"); var attachment = new Attachment(new MemoryStream(attachBodyBytes), "text/plain"); string attachmentName = "test_attachment.txt"; rev.AddAttachment(attachment, attachmentName); rev.Save(); // do query that finds that doc with prefetch var view = database.GetView("aview"); view.SetMapReduce((IDictionary<string, object> document, EmitDelegate emitter)=> { var id = (string)document["_id"]; emitter(id, null); }, null, "1"); // try to get the attachment var query = view.CreateQuery(); query.Prefetch=true; var results = query.Run(); foreach(var row in results) { // This returns the revision just fine, but the sequence number // is set to 0. var revision = row.Document.CurrentRevision; //var attachments = revision.AttachmentNames.ToList(); // This returns an Attachment object which looks ok, except again // its sequence number is 0. The metadata property knows about // the length and mime type of the attachment. It also says // "stub" -> "true". var attachmentRetrieved = revision.GetAttachment(attachmentName); var inputStream = attachmentRetrieved.ContentStream; Assert.IsNotNull(inputStream); var attachmentDataRetrieved = attachmentRetrieved.Content.ToArray(); var attachmentDataRetrievedString = Runtime.GetStringForBytes(attachmentDataRetrieved); var attachBodyString = Sharpen.Runtime.GetStringForBytes(attachBodyBytes); Assert.AreEqual(attachBodyString, attachmentDataRetrievedString); // Cleanup attachmentRetrieved.Dispose(); } // Cleanup. attachment.Dispose(); }
internal Attachment GetAttachmentForSequence (long sequence, string filename) { Debug.Assert((sequence > 0)); Debug.Assert((filename != null)); Cursor cursor = null; var args = new [] { Convert.ToString(sequence), filename }; try { cursor = StorageEngine.RawQuery("SELECT key, type FROM attachments WHERE sequence=? AND filename=?", args); if (!cursor.MoveToNext()) { throw new CouchbaseLiteException(StatusCode.NotFound); } var keyData = cursor.GetBlob(0); //TODO add checks on key here? (ios version) var key = new BlobKey(keyData); var contentStream = Attachments.BlobStreamForKey(key); if (contentStream == null) { Log.E(Tag, "Failed to load attachment"); throw new CouchbaseLiteException(StatusCode.InternalServerError); } else { var result = new Attachment(contentStream, cursor.GetString(1)); result.Compressed = Attachments.IsGZipped(key); return result; } } catch (SQLException) { throw new CouchbaseLiteException(StatusCode.InternalServerError); } finally { if (cursor != null) { cursor.Close(); } } }
internal void AddAttachment(Attachment attachment, string name) { IDictionary<string, object> attachments = (IDictionary<string, object>)properties .Get("_attachments"); if (attachments == null) { attachments = new Dictionary<string, object>(); } attachments.Put(name, attachment); properties.Put("_attachments", attachments); if (attachment != null) { attachment.SetName(name); attachment.SetRevision(this); } }
public void SetAttachment(string name, string contentType, InputStream contentStream ) { Attachment attachment = new Attachment(contentStream, contentType); AddAttachment(attachment, name); }
/// <summary> /// Sets the attachment with the given name. /// </summary> /// <remarks> /// Sets the <see cref="Couchbase.Lite.Attachment"/> with the given name. /// The <see cref="Couchbase.Lite.Attachment"/> data will be written to /// the <see cref="Couchbase.Lite.Database"/> when the /// <see cref="Couchbase.Lite.Revision"/> is saved. /// </remarks> /// <param name="name">The name of the <see cref="Couchbase.Lite.Attachment"/> to set.</param> /// <param name="contentType">The content-type of the <see cref="Couchbase.Lite.Attachment"/>.</param> /// <param name="content">The <see cref="Couchbase.Lite.Attachment"/> content.</param> public void SetAttachment(String name, String contentType, Stream content) { var attachment = new Attachment(content, contentType); AddAttachment(attachment, name); }
/// <summary> /// Sets the attachment with the given name. /// </summary> /// <remarks> /// Sets the <see cref="Couchbase.Lite.Attachment"/> with the given name. /// The <see cref="Couchbase.Lite.Attachment"/> data will be written to /// the <see cref="Couchbase.Lite.Database"/> when the /// <see cref="Couchbase.Lite.Revision"/> is saved. /// </remarks> /// <param name="name">The name of the <see cref="Couchbase.Lite.Attachment"/> to set.</param> /// <param name="contentType">The content-type of the <see cref="Couchbase.Lite.Attachment"/>.</param> /// <param name="content">The <see cref="Couchbase.Lite.Attachment"/> content.</param> public void SetAttachment(String name, String contentType, IEnumerable<Byte> content) { var attachment = new Attachment(new MemoryStream(content.ToArray()), contentType); AddAttachment(attachment, name); }
public Attachment GetAttachmentForSequence(long sequence, string filename) { System.Diagnostics.Debug.Assert((sequence > 0)); System.Diagnostics.Debug.Assert((filename != null)); Cursor cursor = null; string[] args = new string[] { System.Convert.ToString(sequence), filename }; try { cursor = database.RawQuery("SELECT key, type FROM attachments WHERE sequence=? AND filename=?" , args); if (!cursor.MoveToNext()) { throw new CouchbaseLiteException(Status.NotFound); } byte[] keyData = cursor.GetBlob(0); //TODO add checks on key here? (ios version) BlobKey key = new BlobKey(keyData); InputStream contentStream = attachments.BlobStreamForKey(key); if (contentStream == null) { Log.E(Database.Tag, "Failed to load attachment"); throw new CouchbaseLiteException(Status.InternalServerError); } else { Attachment result = new Attachment(contentStream, cursor.GetString(1)); result.SetGZipped(attachments.IsGZipped(key)); return result; } } catch (SQLException) { throw new CouchbaseLiteException(Status.InternalServerError); } finally { if (cursor != null) { cursor.Close(); } } }
/// <summary>https://github.com/couchbase/couchbase-lite-android/issues/134</summary> /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> /// <exception cref="System.IO.IOException"></exception> public virtual void TestGetAttachmentBodyUsingPrefetch() { // add a doc with an attachment Document doc = database.CreateDocument(); UnsavedRevision rev = doc.CreateRevision(); IDictionary<string, object> properties = new Dictionary<string, object>(); properties.Put("foo", "bar"); rev.SetUserProperties(properties); byte[] attachBodyBytes = Sharpen.Runtime.GetBytesForString("attach body"); Attachment attachment = new Attachment(new ByteArrayInputStream(attachBodyBytes), "text/plain"); string attachmentName = "test_attachment.txt"; rev.AddAttachment(attachment, attachmentName); rev.Save(); // do query that finds that doc with prefetch View view = database.GetView("aview"); view.SetMapReduce(new _Mapper_432(), null, "1"); // try to get the attachment Query query = view.CreateQuery(); query.SetPrefetch(true); QueryEnumerator results = query.Run(); while (results.HasNext()) { QueryRow row = results.Next(); // This returns the revision just fine, but the sequence number // is set to 0. SavedRevision revision = row.GetDocument().GetCurrentRevision(); IList<string> attachments = revision.GetAttachmentNames(); // This returns an Attachment object which looks ok, except again // its sequence number is 0. The metadata property knows about // the length and mime type of the attachment. It also says // "stub" -> "true". Attachment attachmentRetrieved = revision.GetAttachment(attachmentName); // This throws a CouchbaseLiteException with Status.NOT_FOUND. InputStream @is = attachmentRetrieved.GetContent(); NUnit.Framework.Assert.IsNotNull(@is); byte[] attachmentDataRetrieved = TextUtils.Read(@is); string attachmentDataRetrievedString = Sharpen.Runtime.GetStringForBytes(attachmentDataRetrieved ); string attachBodyString = Sharpen.Runtime.GetStringForBytes(attachBodyBytes); NUnit.Framework.Assert.AreEqual(attachBodyString, attachmentDataRetrievedString); } }
/// <exception cref="System.Exception"></exception> public virtual void TestAttachments() { string testAttachmentName = "test_attachment"; BlobStore attachments = database.Attachments; NUnit.Framework.Assert.AreEqual(0, attachments.Count()); NUnit.Framework.Assert.AreEqual(new HashSet <object>(), attachments.AllKeys()); Status status = new Status(); IDictionary <string, object> rev1Properties = new Dictionary <string, object>(); rev1Properties["foo"] = 1; rev1Properties["bar"] = false; RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties, database), null, false, status); NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode()); byte[] attach1 = Sharpen.Runtime.GetBytesForString("This is the body of attach1"); database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1 ), rev1.Sequence, testAttachmentName, "text/plain", rev1.GetGeneration()); NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode()); Attachment attachment = database.GetAttachmentForSequence(rev1.Sequence, testAttachmentName ); NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType()); byte[] data = IOUtils.ToByteArray(attachment.GetContent()); NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data)); IDictionary <string, object> innerDict = new Dictionary <string, object>(); innerDict["content_type"] = "text/plain"; innerDict["digest"] = "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE="; innerDict["length"] = 27; innerDict["stub"] = true; innerDict["revpos"] = 1; IDictionary <string, object> attachmentDict = new Dictionary <string, object>(); attachmentDict[testAttachmentName] = innerDict; IDictionary <string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent (rev1.Sequence, EnumSet.NoneOf <TDContentOptions>()); NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence); RevisionInternal gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1 .GetRevId(), EnumSet.NoneOf <TDContentOptions>()); IDictionary <string, object> gotAttachmentDict = (IDictionary <string, object>)gotRev1 .Properties["_attachments"]; NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict); // Check the attachment dict, with attachments included: Sharpen.Collections.Remove(innerDict, "stub"); innerDict.Put("data", Base64.EncodeBytes(attach1)); attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1 .Sequence, EnumSet.Of(TDContentOptions.TDIncludeAttachments)); NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence); gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), EnumSet .Of(TDContentOptions.TDIncludeAttachments)); gotAttachmentDict = (IDictionary <string, object>)gotRev1.Properties.Get("_attachments" ); NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict); // Add a second revision that doesn't update the attachment: IDictionary <string, object> rev2Properties = new Dictionary <string, object>(); rev2Properties.Put("_id", rev1.GetDocId()); rev2Properties["foo"] = 2; rev2Properties["bazz"] = false; RevisionInternal rev2 = database.PutRevision(new RevisionInternal(rev2Properties, database), rev1.GetRevId(), false, status); NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode()); database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1.GetSequence (), rev2.Sequence); // Add a third revision of the same document: IDictionary <string, object> rev3Properties = new Dictionary <string, object>(); rev3Properties.Put("_id", rev2.GetDocId()); rev3Properties["foo"] = 2; rev3Properties["bazz"] = false; RevisionInternal rev3 = database.PutRevision(new RevisionInternal(rev3Properties, database), rev2.GetRevId(), false, status); NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode()); byte[] attach2 = Sharpen.Runtime.GetBytesForString("<html>And this is attach2</html>" ); database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach2 ), rev3.Sequence, testAttachmentName, "text/html", rev2.GetGeneration()); // Check the 2nd revision's attachment: Attachment attachment2 = database.GetAttachmentForSequence(rev2.Sequence, testAttachmentName ); NUnit.Framework.Assert.AreEqual("text/plain", attachment2.GetContentType()); data = IOUtils.ToByteArray(attachment2.GetContent()); NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data)); // Check the 3rd revision's attachment: Attachment attachment3 = database.GetAttachmentForSequence(rev3.Sequence, testAttachmentName ); NUnit.Framework.Assert.AreEqual("text/html", attachment3.GetContentType()); data = IOUtils.ToByteArray(attachment3.GetContent()); NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach2, data)); // Examine the attachment store: NUnit.Framework.Assert.AreEqual(2, attachments.Count()); ICollection <BlobKey> expected = new HashSet <BlobKey>(); expected.AddItem(BlobStore.KeyForBlob(attach1)); expected.AddItem(BlobStore.KeyForBlob(attach2)); NUnit.Framework.Assert.AreEqual(expected, attachments.AllKeys()); status = database.Compact(); // This clears the body of the first revision NUnit.Framework.Assert.AreEqual(StatusCode.Ok, status.GetCode()); NUnit.Framework.Assert.AreEqual(1, attachments.Count()); ICollection <BlobKey> expected2 = new HashSet <BlobKey>(); expected2.AddItem(BlobStore.KeyForBlob(attach2)); NUnit.Framework.Assert.AreEqual(expected2, attachments.AllKeys()); }
/// <exception cref="System.Exception"></exception> public virtual void TestPutLargeAttachment() { string testAttachmentName = "test_attachment"; BlobStore attachments = database.Attachments; attachments.DeleteBlobs(); NUnit.Framework.Assert.AreEqual(0, attachments.Count()); Status status = new Status(); IDictionary <string, object> rev1Properties = new Dictionary <string, object>(); rev1Properties["foo"] = 1; rev1Properties["bar"] = false; RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties, database), null, false, status); NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode()); StringBuilder largeAttachment = new StringBuilder(); for (int i = 0; i < Database.kBigAttachmentLength; i++) { largeAttachment.Append("big attachment!"); } byte[] attach1 = Sharpen.Runtime.GetBytesForString(largeAttachment.ToString()); database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1 ), rev1.Sequence, testAttachmentName, "text/plain", rev1.GetGeneration()); Attachment attachment = database.GetAttachmentForSequence(rev1.Sequence, testAttachmentName ); NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType()); byte[] data = IOUtils.ToByteArray(attachment.GetContent()); NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data)); EnumSet <TDContentOptions> contentOptions = EnumSet.Of(TDContentOptions .TDIncludeAttachments, TDContentOptions.TDBigAttachmentsFollow); IDictionary <string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent (rev1.Sequence, contentOptions); IDictionary <string, object> innerDict = (IDictionary <string, object>)attachmentDictForSequence [testAttachmentName]; if (!innerDict.ContainsKey("stub")) { throw new RuntimeException("Expected attachment dict to have 'stub' key"); } if (((bool)innerDict["stub"]) == false) { throw new RuntimeException("Expected attachment dict 'stub' key to be true"); } if (!innerDict.ContainsKey("follows")) { throw new RuntimeException("Expected attachment dict to have 'follows' key"); } RevisionInternal rev1WithAttachments = database.GetDocumentWithIDAndRev(rev1.GetDocId (), rev1.GetRevId(), contentOptions); // Map<String,Object> rev1PropertiesPrime = rev1WithAttachments.Properties; // rev1PropertiesPrime.put("foo", 2); IDictionary <string, object> rev1WithAttachmentsProperties = rev1WithAttachments.GetProperties (); IDictionary <string, object> rev2Properties = new Dictionary <string, object>(); rev2Properties.Put("_id", rev1WithAttachmentsProperties["_id"]); rev2Properties["foo"] = 2; RevisionInternal newRev = new RevisionInternal(rev2Properties, database); RevisionInternal rev2 = database.PutRevision(newRev, rev1WithAttachments.GetRevId (), false, status); NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode()); database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1WithAttachments .Sequence, rev2.Sequence); // Check the 2nd revision's attachment: Attachment rev2FetchedAttachment = database.GetAttachmentForSequence(rev2.GetSequence (), testAttachmentName); NUnit.Framework.Assert.AreEqual(attachment.GetLength(), rev2FetchedAttachment.GetLength ()); NUnit.Framework.Assert.AreEqual(attachment.GetMetadata(), rev2FetchedAttachment.GetMetadata ()); NUnit.Framework.Assert.AreEqual(attachment.GetContentType(), rev2FetchedAttachment .GetContentType()); // Add a third revision of the same document: IDictionary <string, object> rev3Properties = new Dictionary <string, object>(); rev3Properties.Put("_id", rev2.Properties["_id"]); rev3Properties["foo"] = 3; rev3Properties["baz"] = false; RevisionInternal rev3 = new RevisionInternal(rev3Properties, database); rev3 = database.PutRevision(rev3, rev2.GetRevId(), false, status); NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode()); byte[] attach3 = Sharpen.Runtime.GetBytesForString("<html><blink>attach3</blink></html>" ); database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach3 ), rev3.Sequence, testAttachmentName, "text/html", rev3.GetGeneration()); // Check the 3rd revision's attachment: Attachment rev3FetchedAttachment = database.GetAttachmentForSequence(rev3.GetSequence (), testAttachmentName); data = IOUtils.ToByteArray(rev3FetchedAttachment.GetContent()); NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach3, data)); NUnit.Framework.Assert.AreEqual("text/html", rev3FetchedAttachment.GetContentType ()); // TODO: why doesn't this work? // Assert.assertEquals(attach3.length, rev3FetchedAttachment.getLength()); ICollection <BlobKey> blobKeys = database.Attachments.AllKeys(); NUnit.Framework.Assert.AreEqual(2, blobKeys.Count); database.Compact(); blobKeys = database.Attachments.AllKeys(); NUnit.Framework.Assert.AreEqual(1, blobKeys.Count); }
/// <summary> /// Sets the attachment with the given name. /// </summary> /// <remarks> /// Sets the <see cref="Couchbase.Lite.Attachment"/> with the given name. /// The <see cref="Couchbase.Lite.Attachment"/> data will be written to /// the <see cref="Couchbase.Lite.Database"/> when the /// <see cref="Couchbase.Lite.Revision"/> is saved. /// </remarks> /// <param name="name">The name of the <see cref="Couchbase.Lite.Attachment"/> to set.</param> /// <param name="contentType">The content-type of the <see cref="Couchbase.Lite.Attachment"/>.</param> /// <param name="content">The <see cref="Couchbase.Lite.Attachment"/> content.</param> public void SetAttachment(string name, string contentType, IEnumerable<byte> content) { var data = content?.ToArray(); if(data == null) { AddAttachment(null, name); return; } var stream = RecyclableMemoryStreamManager.SharedInstance.GetStream("UnsavedRevision", data, 0, data.Length); var attachment = new Attachment(stream, contentType); AddAttachment(attachment, name); }