【问题标题】:split binary file into chunks or Parts upload / download将二进制文件拆分为块或部分上传/下载
【发布时间】:2021-08-31 12:35:28
【问题描述】:

出于某种原因,我使用 couchdb 作为内容管理来将文件作为二进制数据上传,没有像 mongoDB 这样的 GridFs 支持来上传大文件,所以我需要将文件作为块上传,然后将它们作为一个文件检索。

这是我的代码

  public string InsertDataToCouchDb(string dbName, string id, string filename, byte[] image)
  {
    var connection = System.Configuration.ConfigurationManager.ConnectionStrings["CouchDb"].ConnectionString;
    using (var db = new MyCouchClient(connection, dbName))
    {

      // HERE I NEED TO UPLOAD MY IMAGE BYTE[] AS CHUNKS 
      var artist = new couchdb
      {
        _id = id,
        filename = filename,
        Image = image
      };

      var response = db.Entities.PutAsync(artist);
      return response.Result.Content._id;
    }
  }

  public byte[] FetchDataFromCouchDb(string dbName, string id)
  {
    var connection = System.Configuration.ConfigurationManager.ConnectionStrings["CouchDb"].ConnectionString;
    using (var db = new MyCouchClient(connection, dbName))
    {
      //HERE I NEED TO RETRIVE MY FULL IMAGE[] FROM CHUNKS
      var test = db.Documents.GetAsync(id, null);
      var doc = db.Serializer.Deserialize<couchdb>(test.Result.Content);
      return doc.Image;
    }
  }

谢谢你

【问题讨论】:

  • 您正在进行异步调用,但未使用await。这甚至不会编译?
  • 另外,将图像存储在文档中而不是附件中是一个糟糕的设计选择。特别是如果图像的大小不是微不足道的!

标签: c# asp.net asp.net-core couchdb


【解决方案1】:

将图像数据放入 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
  1. 创建一个名为“MyCouchUploader”的新 .Net Core 控制台应用项目和解决方案
  2. 将使用 PR 177 拉取的 MyCouch 项目添加到解决方案中
  3. 将 MyCouch 项目添加为 MyCouchUploader 依赖项
  4. 将 Nuget 包“Microsoft.AspNetCore.StaticFiles”添加为 MyCouchUploader 依赖项
  5. 将 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 直观地验证文档的附件。

【讨论】:

  • 有大约 1 GB 大小的 PDF 文件需要在有效时间内通过批量导入上传到 CouchDB。所以如果你能在这个要求上指导我
  • 1G?哇,这令人印象深刻。通过byte[] 传递1G 数据...如果使用MyCouch,肯定需要使用PR 177Attachment.PutAsync。将这种大小的资产存储在 CouchDB 文档的正文中将是一个重大错误。我觉得我的回答很好 - 您的后续问题适用于服务器配置和系统架构,而不是编程,因此最好在 ServerFaultSoftware Engineering 上提问。祝你好运!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-23
  • 2017-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多