diff --git a/LoveSeat.IntegrationTest/CouchClientTest.cs b/LoveSeat.IntegrationTest/CouchClientTest.cs index b83d29a..228aa30 100644 --- a/LoveSeat.IntegrationTest/CouchClientTest.cs +++ b/LoveSeat.IntegrationTest/CouchClientTest.cs @@ -128,6 +128,25 @@ public void Should_Return_Attachment_Names() Assert.IsTrue(doc.GetAttachmentNames().Contains("martin.jpg")); } + [Test] + public void Should_UrlEncode_Attachment_Names() + { + // Attachment filenames should be URL-encoded to allow them to be round-tripped. + // e.g. given a filename of "url%20encoded.jpg", note that it is already url-encoded ('%20' is the encoded form of ' '). + // Thus, in order to preserve the filename, it must be url-encoded an additional time. + + var db = client.GetDatabase(baseDatabase); + db.CreateDocument(@"{""_id"":""upload""}"); + var attachment = File.ReadAllBytes("../../Files/martin.jpg"); + db.AddAttachment("upload", attachment, "url%20encoded.jpg", "image/jpeg"); + db.AddAttachment("upload", attachment, "not url encoded.jpg", "image/jpeg"); + var doc = db.GetDocument("upload"); + var attachments = doc.GetAttachmentNames().ToList(); + foreach (var filename in attachments) { System.Diagnostics.Trace.WriteLine(filename); } + Assert.IsTrue(attachments.Contains("url%20encoded.jpg"), "Attachments with filenames that are already url-encoded should be encoded to round-trip their name accurately."); + Assert.IsTrue(attachments.Contains("not url encoded.jpg"), "Attachments with non-url-encoded filenames should be persisted accurately."); + } + [Test] public void Should_Create_Admin_User() { diff --git a/LoveSeat/CouchDatabase.cs b/LoveSeat/CouchDatabase.cs index b40d698..d1f2907 100644 --- a/LoveSeat/CouchDatabase.cs +++ b/LoveSeat/CouchDatabase.cs @@ -213,7 +213,7 @@ public CouchResponseObject AddAttachment(string id, byte[] attachment, string fi public CouchResponseObject AddAttachment(string id, string rev, byte[] attachment, string filename, string contentType) { return - GetRequest(string.Format("{0}/{1}/{2}?rev={3}", databaseBaseUri, id, filename, rev)).Put().ContentType(contentType).Data(attachment).GetCouchResponse().GetJObject(); + GetRequest(string.Format("{0}/{1}/{2}?rev={3}", databaseBaseUri, id, HttpUtility.UrlEncode(filename), rev)).Put().ContentType(contentType).Data(attachment).GetCouchResponse().GetJObject(); } /// /// Adds an attachment to a document. If revision is not specified then the most recent will be fetched and used. Warning: if you need document update conflicts to occur please use the method that specifies the revision @@ -238,7 +238,7 @@ public CouchResponseObject AddAttachment(string id, Stream attachmentStream, str public CouchResponseObject AddAttachment(string id, string rev, Stream attachmentStream, string filename, string contentType) { return - GetRequest(string.Format("{0}/{1}/{2}?rev={3}", databaseBaseUri, id, filename, rev)).Put().ContentType(contentType).Data(attachmentStream).GetCouchResponse().GetJObject(); + GetRequest(string.Format("{0}/{1}/{2}?rev={3}", databaseBaseUri, id, HttpUtility.UrlEncode(filename), rev)).Put().ContentType(contentType).Data(attachmentStream).GetCouchResponse().GetJObject(); } public Stream GetAttachmentStream(Document doc, string attachmentName)