小弟前段试着利用Bytea类型存取大对象,但是程序编好后测试发现虽然一个butea的属性可以存储高达1GB的二进制数据,但是它需要占用大量内存来完成这个大数据,实际中远达不到1G,上传一个大于13M的文件就会出现out of memory 或者connection is broken的异常,相当郁闷。当然也有可能是我程序的原因。
    现在正在初试用oid这个类型来存取,修改程序后运行,却老出现invalid large-object descriptor: 0的异常,相当的郁闷闷!希望懂这方面的好心高手帮帮小弟,先谢谢您的大恩啦!QQ一直在线:254380172
    private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == DialogResult.OK)
            fullfilename = ofd.FileName.ToString();
            filename = Path.GetFileName(fullfilename);
            NpgsqlConnection conn = new NpgsqlConnection("server=127.0.0.1;database=mydb;uid=postgres;pwd=1234");
            conn.Open();
            NpgsqlTransaction transaction = conn.BeginTransaction();
            LargeObjectManager lom = new LargeObjectManager(conn);
            conn.Open();
            int noid = lom.Create(LargeObjectManager.READWRITE);
            LargeObject lo = lom.Open(noid, LargeObjectManager.WRITE);
            FileStream fs = File.OpenRead(fullfilename);
            byte[] buf = new byte[fs.Length];
            fs.Read(buf, 0, (int)fs.Length);
            MessageBox.Show(conn.State.ToString());
            lo.Write(buf);//错误提示出:ERROR: 42704: invalid large-object descriptor: 0
            fs.Close();
            lo.Close();
            NpgsqlCommand command = new NpgsqlCommand("insert into file2(name, content) values(@name, @content)", conn);
            command.Parameters.Add(new NpgsqlParameter("@name", NpgsqlTypes.NpgsqlDbType.Text));
            command.Parameters.Add(new NpgsqlParameter("@content", NpgsqlTypes.NpgsqlDbType.Integer));
            command.Parameters["@name"].Value = filename;
            command.Parameters["@content"].Value = noid;
            command.ExecuteNonQuery();
            transaction.Commit();
            conn.Close();
            MessageBox.Show("上传文件成功!");
        }
      
        private void button2_Click(object sender, EventArgs e)
        {
            SaveFileDialog sfd = new SaveFileDialog();
            if (sfd.ShowDialog() == DialogResult.OK)
            fullfilename = sfd.FileName.ToString();
            filename = Path.GetFileName(fullfilename);
            NpgsqlConnection conn = new NpgsqlConnection("server=127.0.0.1;database=mydb;uid=postgres;pwd=1234");
            conn.Open();
            NpgsqlTransaction transaction = conn.BeginTransaction();
            LargeObjectManager lom = new LargeObjectManager(conn);
            conn.Open();
            //NpgsqlDataAdapter sda = new NpgsqlDataAdapter("select * from file2 where name = '" + filename + "'", conn);
            //DataSet ds = new DataSet();
            //sda.Fill(ds, "file2");
            //int noid = (int)ds.Tables["file2"].Rows[0][1];
            NpgsqlCommand cmd = new NpgsqlCommand("select content from file2 where name = '" + filename + "'", conn);
            NpgsqlDataReader sdr = cmd.ExecuteReader();
            int noid = sdr.GetInt32(1);
            LargeObject lo = lom.Open(noid, LargeObjectManager.READ);
            FileStream fs = File.OpenWrite(fullfilename);
            byte[] buf = new byte[lo.Size()];
            buf = lo.Read(lo.Size());
            fs.Write(buf, 0, (int)lo.Size());
            fs.Flush();
            fs.Close();
            lo.Close();
            transaction.Commit();
            DeleteLargeObject(noid);
            //MemoryStream ms = new MemoryStream(buf);
            //pictureBox1.Image = Image.FromStream(ms);
            conn.Close();
            MessageBox.Show("上传文件成功!");
        }
        public static void DeleteLargeObject(Int32 noid)
        {
            NpgsqlConnection conn = new NpgsqlConnection("server=127.0.0.1;database=mydb;uid=postgres;pwd=1234");
            conn.Open();
            NpgsqlTransaction t = conn.BeginTransaction();
            LargeObjectManager lom = new LargeObjectManager(conn);
            lom.Delete(noid);
            t.Commit();
            conn.Close();        }

解决方案 »

  1.   

    帮助小弟解决问题者,分数立马送出!还请大家多多帮忙啊!
    UP UP UP UP UP UP UP UP UP UP UP UP 
      

  2.   

    pgsql 的 oid 类型不是用来存二进制数据的
      

  3.   

    LOB 字段的存取在 Npgsql 的 docs\UserManual.html 里头有例子
      

  4.   

    三楼的花蝴蝶,你好,我的程序就是参考着UserManual给的例子编的啊,如果可以的话,请指出具体是哪个地方编写的不对或者是有问题,谢谢
      

  5.   

    你的 npgsql 是哪个版本的, 我的 2.0.6 测试了例子 , 没事的
      

  6.   

    不好意思,您发给我的好像没有npgsql.dll。我把我的例子程序发给你吧,您测试一下看看。已发到您邮箱,请查收!
      

  7.   

    你的例子测试没问题,你看看       int noid = lom.Create(LargeObjectManager.READWRITE);
    这个返回多少, 是不是 0, 这个应该返回一个 > 0 的数值才对
      

  8.   

        花蝴蝶,非常感谢你!invalid large-object descriptor: 0的异常已经解决了!不知您测试了没,但是我测试时发现如果上传一个90多M的文件就会出现connection is broken的异常!大文件还是传不上呀!
        
        期待回复
      

  9.   

    当大于90兆时, 我这里也报那个异常, 查了一下, 是在读 large object 到本地时发生的,
    代码:buf = lo.Read(lo.Size()); // 这一句导致的修改为一次读取一个缓冲区大小, 写入文件, 循环直到结束, 就没事了我的垃圾代码片断:      LargeObject lo = lbm.Open(noid, LargeObjectManager.READWRITE);      FileStream fsout = File.OpenWrite("D:\\study.dbf.database");
          int readed = 0;
          int loPos = 0;
          lo.Seek(0);
          byte[] tempBuf = null;
          do
          {
            tempBuf = lo.Read(4096);
            readed = tempBuf.Length;
            if (readed > 0)
            {
              fsout.Write(tempBuf, 0, readed);
              loPos += readed;
              lo.Seek(loPos);
            }
          } while (readed > 0);
          fsout.Flush();
          fsout.Close();
          lo.Close();
          t.Commit();
      

  10.   

        哦,块式存储,我想如果数据库字段换成bytea的话,是不是也同样能解决大文件不能上传的问题,一会试试,哈哈!
        想问一下,您用昨天的代码上传一个大于90M的文件是没出错,而只有当读取它的时候才会出现异常吗?我再上传的时候就出现异常了!您的程序能打包发给我一份,让小弟好好学习学习吗?
      

  11.   

    我的程序也是抄的文档的那个例子, 我的是上传没问题, 下载有问题,估计这个跟具体环境: 服务器环境, 客户端环境有关了.large object 的 上传, 下载都支持那个块式存储, 最好上传下载都用这种方式?
      

  12.   

        花蝴蝶,刚找到了上传那块出现异常的原因了:弹出一个“查找源FastPath.cs”的对话框。是不是您给的DLL文件里缺这个东东的意思?
         还有刚才试着把上传那块代码改成块式存储,由于小弟水平有限,未能调试成功!能不能发个上传的块式存储代码,让我好好学习学习?
      

  13.   

    fastpath, large object manager 那个 dll 里头都有的, 这部分代码是从 jdbc port 过来的.我试试块上传
      

  14.   


        // 上传
        void button18_Click(object sender, EventArgs e)
        {
          string arg = @"H:\ftp\D7Items.tar";
          int bufSize = 4096;
          using (NpgsqlConnection newcon = new NpgsqlConnection(UtilClass.connectionstring))
          {
            newcon.Open();
            using (NpgsqlTransaction t = newcon.BeginTransaction())
            {
              LargeObjectManager lbm = new LargeObjectManager(newcon);          int noid = lbm.Create(LargeObjectManager.READWRITE);
              LargeObject lo = lbm.Open(noid, LargeObjectManager.WRITE);          FileStream fs = File.OpenRead(arg);
              byte[] buf = new byte[bufSize];
              int readIn = 0;
              do
              {
                readIn = fs.Read(buf, 0, bufSize);
                lo.Write(buf);
              } while (readIn > 0);
              fs.Close();
              lo.Close();
              t.Commit();
              txtLobId.Text = noid.ToString();
            }
            newcon.Close();
          }
        }
        static void DeleteLargeObject(Int32 noid)
        {
          NpgsqlConnection conn = new NpgsqlConnection(UtilClass.connectionstring);      conn.Open();
          NpgsqlTransaction t = conn.BeginTransaction();
          LargeObjectManager lbm = new LargeObjectManager(conn);
          lbm.Delete(noid);      t.Commit();      conn.Close();    }    // lob download
        void button19_Click(object sender, EventArgs e)
        {
          int loid = 0;
          if (string.IsNullOrEmpty(txtLobId.Text))
            return;
          if (!int.TryParse(txtLobId.Text, out loid))
            return;
          using (NpgsqlConnection newcon = new NpgsqlConnection(UtilClass.connectionstring))
          {
            newcon.Open();
            using (NpgsqlTransaction t = newcon.BeginTransaction())
            {
              LargeObjectManager lbm = new LargeObjectManager(newcon);
              LargeObject lo = lbm.Open(loid, LargeObjectManager.READWRITE);
              FileStream fsout = File.OpenWrite("D:\\temp.dat");
              int readed = 0;
              int loPos = 0;
              lo.Seek(0);
              byte[] tempBuf = null;
              do
              {
                tempBuf = lo.Read(4096);
                readed = tempBuf.Length;
                if (readed > 0)
                {
                  fsout.Write(tempBuf, 0, readed);
                  loPos += readed;
                  lo.Seek(loPos);
                }
              } while (readed > 0);
              fsout.Flush();
              fsout.Close();
              lo.Close();
              t.Commit();
            }
          }
        }    // log remove
        void button20_Click(object sender, EventArgs e)
        {
          int loid = 0;
          if (string.IsNullOrEmpty(txtLobId.Text))
            return;
          if (!int.TryParse(txtLobId.Text, out loid))
            return;      DeleteLargeObject(loid);
        }
    4096 有点慢, 你试试不同缓冲区大小,
    我用了一个 123 兆的, 上传, 下载后, 还可以打开.
      

  15.   

        非常感谢你的帮助,上传问题已搞定!但是不知道您注意到没,我在测试过程中发现:当在程序中删除大对象后,数据库非但没有减小,反而越变越大了!难道DeleteLargeObject(loid)没有起到作用,请问应怎样删除?
        还有想问一下,大对象的实际内容看资料上说应该是存到一个pg_largeobject的系统表里,但我看了看我的数据库里都没有这个表,那么它存到什么地方了?能查看到它吗?能手动删除吗?
      

  16.   

        现在还发现小文件只在用户表里存了一个OID值,而pg_largeobject表里则没有它的记录。如果存一个大文件,则用户表和pg_largeobject表里都会有与之相关的记录!请问小文件的实际内容存到哪里了?
      

  17.   

    有 pg_largeobject 这个系统表的,
    在 psql 中 \d pg_largeobject 可以看到这个表的定义,看文档说, 她将文件分割成一系列的 chunk 放到了这个表中, 实际存放文件内容的字段类型是 bytea!用户表那个 oid 的维护应该是你的责任吧?我的调用 deleteLargeObject 后, 数据库目录变小了, 你试试  VACUUM 一下?
      

  18.   

        花蝴蝶,VERY THANKS !问题已解决!你真是个好人啊!太感激你啦!
        再请教一下,删除数据前,是不是还要执行以下lo_unlink()呀?应怎样执行?
        我的表file2(name text,content int)
      

  19.   

    unlink 和 delete 等同, unlink 是为接口一致性考虑提供的接口就是 , open / read / write / close / unlink, ...起源于 unix / C I/O 规范.
      

  20.   

    题外,  PostgreSQL 是很好的数据库.
      

  21.   

    恩,赞同!
             PostgreSQL很开源,很强大!
             Mr.花,THANK YOU!