zoukankan      html  css  js  c++  java
  • 如何将大文件写入到数据库中

    最近利用空闲时间自己在写一个文件备份工具,因为我磁盘上的很多文件很重要,例如很多PPT和讲义。所以需要经常备份,而且因为这些文件很多,所以需要增量备份。

    我尝试用过windows自带的ntbackup工具,但感觉不是很爽。它不支持压缩备份,而且界面也有点复杂。

    为了响应伟大领袖的“自力更生,丰衣足食”的号召,咱决定自己写一个工具,专门备份到数据库。支持压缩,支持加密,支持增量。

    本文分享一下其中一些重点的技术细节

    其中一个关键的技术就是将文件使用二进制的方式存放在数据库的varbinary(max)的字段中。该字段最大允许的长度为2GB。

    对于一些小文件,我们可以一次性读取它的所有字节,然后一次提交到数据库

    /// <summary>
    /// 这个方法演示了如何一次提交所有的字节。这样导致的结果是:应用程序立即需要申请等同于文件大小的内存
    /// </summary>
    static void SubmitFileByOnce() {
    string file = @"F:功夫熊猫.rmvb";//文件大小为519MB
    byte[] buffer = File.ReadAllBytes(file);
    using (SqlConnection conn = new SqlConnection("server=(local);database=demo;integrated security=true")) {
    using (SqlCommand cmd = conn.CreateCommand())
    {
    cmd.CommandText = "INSERT INTO Files(FileName,FileContents) VALUES(@fileName,@fileContents)";
    cmd.Parameters.AddRange(
    new[]
    {
    new SqlParameter("@fileName",file),
    new SqlParameter("@fileContents",buffer)
    });

    conn.Open();
    cmd.ExecuteNonQuery();
    conn.Close();
    }
    }
    }

    但是,上面的方法有几个问题,主要体现在如果文件比较大的话

    1. 它需要一次性很大的内存,具体数据等同于文件大小。因为File.ReadAllBytes方法是将所有字节全部读入到内存。

    2. 它会导致提交失败,就是因为数据太大了。数据库也会拒绝。

    那么,我就对这个方法做了一下改进,将文件拆分为5MB一段,也就是说,此时每次申请的内存只有5MB。这就大大地提高了可用性。

    /// <summary>
    /// 这个方法是将文件切分为5MB的块,每次只是提交5MB,所以可能多次提交,但内存占用就比较小
    /// </summary>
    static void SubmitFileStepByStep() {
    string file = @"F:功夫熊猫.rmvb";//以这个文件为例,大小为519MB,一共需要的时间大约94秒。还是有点慢的,所以还可能需要进行压缩
    FileStream fs = new FileStream(file, FileMode.Open);

    byte[] buffer = new byte[5 * 1024 * 1024];
    int readCount;
    using (SqlConnection conn = new SqlConnection("server=(local);database=demo;integrated security=true"))
    {
    conn.Open();

    while ((readCount = fs.Read(buffer, 0, buffer.Length)) > 0)
    {

    using (SqlCommand cmd = conn.CreateCommand())
    {
    cmd.CommandText = "INSERT INTO Files(FileName,FileContents) VALUES(@fileName,@fileContents)";
    cmd.Parameters.AddRange(
    new[]
    {
    new SqlParameter("@fileName",file),
    new SqlParameter("@fileContents",buffer)
    });

    cmd.ExecuteNonQuery();
    }

    }

    conn.Close();

    }
    }

    这样的话,有一个后果就是一个文件,可能在数据库中会有多条记录。所以在读取的时候,我们需要对其进行合并

    static void DownloadFile() {
    string file = @"F:功夫熊猫.rmvb";
    string destfile = @"E:TempTemp.wmv";
    using (SqlConnection conn = new SqlConnection("server=(local);database=demo;integrated security=true"))
    {
    using (SqlCommand cmd = conn.CreateCommand())
    {
    cmd.CommandText = "SELECT FileContents FROM Files WHERE FileName=@fileName";
    cmd.Parameters.AddRange(
    new[]
    {
    new SqlParameter("@fileName",file),
    });

    conn.Open();
    SqlDataReader reader = cmd.ExecuteReader();
    FileStream fs = new FileStream(destfile, FileMode.Append, FileAccess.Write);

    while (reader.Read())
    {
    byte[] buffer = (byte[])reader[0];
    fs.Write(buffer, 0, buffer.Length);
    }

    fs.Close();
    reader.Close();
    conn.Close();
    }
    }
    }

    本文由作者:陈希章 于 2009/7/28 9:55:30 发布在:http://www.cnblogs.com/chenxizhang/
    本文版权归作者所有,可以转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    更多博客文章,以及作者对于博客引用方面的完整声明以及合作方面的政策,请参考以下站点:陈希章的博客中心

    未能加载文件或程序集“System.Data.SQLite, Version=1.0.66.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139”或它的某一个依赖项。试图加载格式不正确的程序。

    搞半天 原来问题是 SQLite下载的平台类型不对!!我用的是 64 位WIN8 系统 ,而SQLite下载的是X86 32位的!
    出现这个错误可要小心了!

    在VS“项目属性”里的“生成”-》目标平台设置为x86 (不要选择x64和Any CPU)再编译后问题消失!!!!!!!!!!!

  • 相关阅读:
    一分钟认识:Cucumber框架(一)
    迭代=冲刺?
    Nresource服务之接口缓存化
    58集团支付网关设计
    服务治理在资源中心的实践
    资源中心——连接池调优
    4种常用的演讲结构: 黄金圈法则结构、PREP结构、时间轴结构、金字塔结构
    微服务时代,领域驱动设计在携程国际火车票的实践
    Sentinel -- FLOW SLOT核心原理篇
    管理篇-如何跨部门沟通?
  • 原文地址:https://www.cnblogs.com/blogpro/p/11456993.html
Copyright © 2011-2022 走看看