将图像数据放入 CouchDB 文档是一个糟糕的想法。只是不要。这就是CouchDB attachments 的目的。
仅通过文档更新而使用冗余 blob 数据使数据库膨胀的可能性肯定会对除了玩具数据库之外的任何东西产生重大的负面影响。
此外,由于 OP 中的代码正在调用异步方法,例如 async/await 如何工作,似乎缺乏理解。 db.Entities.PutAsync(artist),没有 await - 每次调用肯定会失败(如果编译器甚至允许代码)。我强烈建议阅读 Microsoft 文档 Asynchronous programming with async and await。
现在说到“分块”:如果图像数据太大以至于需要以其他方式流式传输,那么通过字节数组传递它的业务看起来很糟糕。如果图片比较小,直接使用Attachment.PutAsync即可。
虽然 MyCouch v7.6 上的 Attachment.PutAsync 不支持流(有效分块),但存在 Support Streams for attachments #177 PR,它支持,而且看起来还不错。
这是一个单页 C# .Net Core 控制台应用程序,它使用 PR 177 提供的非常有效的流式传输将给定文件作为附件上传到特定文档。虽然代码使用 PR 177,但最重要的是它使用附件来存储 blob 数据.用字节数组替换流是相当简单的。
我的沙发 + PR 177
在控制台中获取 MyCouch 资源,然后应用 PR 177
$ git clone https://github.com/danielwertheim/mycouch.git
$ cd mycouch
$ git pull origin 15a1079502a1728acfbfea89a7e255d0c8725e07
(我不知道 git,所以可能有更好的方法来获得 PR)
我的沙发上传器
用VS2019
- 创建一个名为“MyCouchUploader”的新 .Net Core 控制台应用项目和解决方案
- 将使用 PR 177 拉取的 MyCouch 项目添加到解决方案中
- 将 MyCouch 项目添加为 MyCouchUploader 依赖项
- 将 Nuget 包“Microsoft.AspNetCore.StaticFiles”添加为 MyCouchUploader 依赖项
- 将 Program.cs 的内容替换为以下代码:
using Microsoft.AspNetCore.StaticFiles;
using MyCouch;
using MyCouch.Requests;
using MyCouch.Responses;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace MyCouchUploader
{
class Program
{
static async Task Main(string[] args)
{
// args: scheme, database, file path of asset to upload.
if (args.Length < 3)
{
Console.WriteLine("\nUsage: MyCouchUploader scheme dbname filepath\n");
return;
}
var opts = new
{
scheme = args[0],
dbName = args[1],
filePath = args[2]
};
Action<Response> check = (response) =>
{
if (!response.IsSuccess) throw new Exception(response.Reason);
};
try
{
// canned doc id for this app
const string docId = "SO-68998781";
const string attachmentName = "Image";
DbConnectionInfo cnxn = new DbConnectionInfo(opts.scheme, opts.dbName)
{ // timely fail if scheme is bad
Timeout = TimeSpan.FromMilliseconds(3000)
};
MyCouchClient client = new MyCouchClient(cnxn);
// ensure db is there
GetDatabaseResponse info = await client.Database.GetAsync();
check(info);
// delete doc for succcessive program runs
DocumentResponse doc = await client.Documents.GetAsync(docId);
if (doc.StatusCode == HttpStatusCode.OK)
{
DocumentHeaderResponse del = await client.Documents.DeleteAsync(docId, doc.Rev);
check(del);
}
// sniff file for content type
FileExtensionContentTypeProvider provider = new FileExtensionContentTypeProvider();
if (!provider.TryGetContentType(opts.filePath, out string contentType))
{
contentType = "application/octet-stream";
}
// create a hash for silly verification
using var md5 = MD5.Create();
using Stream stream = File.OpenRead(opts.filePath);
byte[] fileHash = md5.ComputeHash(stream);
stream.Position = 0;
// Use PR 177, sea-locks:stream-attachments.
DocumentHeaderResponse put = await client.Attachments.PutAsync(new PutAttachmentStreamRequest(
docId,
attachmentName,
contentType,
stream // :-D
));
check(put);
// verify
AttachmentResponse verify = await client.Attachments.GetAsync(docId, attachmentName);
check(verify);
if (fileHash.SequenceEqual(md5.ComputeHash(verify.Content)))
{
Console.WriteLine("Atttachment verified.");
}
else
{
throw new Exception(String.Format("Attachment failed verification with status code {0}", verify.StatusCode));
}
}
catch (Exception e)
{
Console.WriteLine("Fail! {0}", e.Message);
}
}
}
}
运行:
$ MyCouchdbUploader http://name:password@localhost:5984 dbname path-to-local-image-file
使用 Fauxton 直观地验证文档的附件。