无法存储然后将PostgreSQL中的文件作为大对象读取

问题描述 投票:1回答:2

我正在尝试使用NpgSQL v3.0.4.0编写和读取PostgreSQL数据库V9.4.x中的大对象。所以我创建了一个方法来将本地文件存储在数据库中作为一个大对象,如下所示:

public static async Task<uint> InsertLargeObjectFileToDB(string theFilePath)
{
     // connecting to DB
     string connstring = MakeDatabaseConnectionString();
     // make a connection object
     NpgsqlConnection Conn = new NpgsqlConnection(connstring);
     try
     {
        await OpenDatabaseConnection(Conn); //open database connection
     }
     catch (Exception Ex)
     {
        throw (Ex);
     }

     uint oid; // to store object ID number
     try
     {
        // Reading and writing Large Objects requires the use of a transaction

        using (FileStream fs = new FileStream(theFilePath, FileMode.Open))
        {
           using (var transaction = Conn.BeginTransaction())
           {
              // Retrieve a Large Object Manager for this connection
              var manager = new NpgsqlLargeObjectManager(Conn);
              // Create a new empty file, returning the identifier to later access it
              oid = manager.Create();

              using (var DbStream = manager.OpenReadWrite(oid))
              {
                 long theFileSize = GetFileSizeInBytes(theFilePath);
                 StreamReader sr = new StreamReader(fs);
                 byte[] buffer = new byte[1024 * 1024];

                 while (sr.BaseStream.Position < theFileSize)
                 {
                    await fs.ReadAsync(buffer, 0, buffer.Length);
                    await DbStream.WriteAsync(buffer, 0, buffer.Length);
                 }
              }
              transaction.Commit();
              return oid;
           }
        }
     }
     catch // any error
     {
        // exception
        Exception ex = new Exception();
        ex.Data.Add(ex.Data.Count, "some error message");
        throw ex;
     }
}

然后我创建了一个方法来读取一个大对象并将其存储在temp目录中随机命名的文件中,如下所示:

public static async Task<string> GetLargeObjectFileFromDB(uint oid)
{
     // connecting to DB
     string connstring = MakeDatabaseConnectionString();
     // make a connection object
     NpgsqlConnection Conn = new NpgsqlConnection(connstring);
     try
     {
        await OpenDatabaseConnection(Conn); //open database connection
     }
     catch (Exception Ex)
     {
        throw (Ex);
     }

     // getting a temorary file name from the system to use it to store the fetched file
     string TempFileName = GetRandomFileNameFromSystem();

     try
     {
        using (FileStream LocalStream = new FileStream(TempFileName, FileMode.Create))
        {
           using (var transaction = Conn.BeginTransaction())
           {
              // create a Large Object Manager for this connection
              var DbLargeObjectManager = new NpgsqlLargeObjectManager(Conn);

              using (var DbStream = await DbLargeObjectManager.OpenReadAsync(oid))
              {
                 byte[] buffer = new byte[1024 * 1024];
                 // get the length of the database object
                 long LengthOfDbObject = DbStream.Length;

                 while (DbStream.Position < LengthOfDbObject)
                 {
                    // read from the database to buffer
                    await DbStream.ReadAsync(buffer, 0, buffer.Length);
                    //write from buffer to local file
                    await LocalStream.WriteAsync(buffer, 0, buffer.Length);
                 }
              }
              transaction.Commit();
              return TempFileName;
           }
        }
     }
     catch // any error
     {
        // exception
        Exception ex = new Exception();
        ex.Data.Add(ex.Data.Count, "Error inserting object in database");
        throw ex;
     }
 }

你可以看到我一直在异步写作。问题是我对这2个方法进行了测试,这个测试将6MB文件写入数据库,但是当我从数据库中再次读取该文件时,它大约大400kb,当然(当然)MD5哈希值不匹配。不要忘记提及没有异常发生的事情。如果你关心它,这是测试:

private async void button3_Click(object sender, EventArgs e)
  {
     listBox1.Items.Clear();

     // getting the MD5 hash of the source file
     string FirstMd5Hash = GetMd5OfFile(tbSourceFile.Text);

     // performance measurment ##########################################
     DateTime dt1 = new DateTime(DateTime.Now.Ticks);
     listBox1.Items.Add("Uploading file to database");
     //storing that file into database
     uint oid = await InsertLargeObjectFileToDB(tbSourceFile.Text);

     // performance measurment #########################################################
     DateTime dt2 = new DateTime(DateTime.Now.Ticks);
     TimeSpan ts = new TimeSpan(dt2.Ticks - dt1.Ticks);
     listBox1.Items.Add("Large object (oid = " + oid + ") inserted in " + ts.Seconds + "." + ts.Milliseconds + " seconds");

     // performance measurment ##########################################
     dt1 = new DateTime(DateTime.Now.Ticks);
     listBox1.Items.Add("reading file back from the database");
     // get that object back from the database into temporary file
     string ReturnedFileName = await PostgresqlLargeObject.GetLargeObjectFileFromDB(oid);
     // performance measurment #########################################################
     dt2 = new DateTime(DateTime.Now.Ticks);
     ts = new TimeSpan(dt2.Ticks - dt1.Ticks);
     listBox1.Items.Add("reading done in " + ts.Seconds + "." + ts.Milliseconds + " seconds");


     //calculate md5 of that file
     string SecondMd5Hash = GetMd5OfFile(ReturnedFileName);

     // compare the 2 hashes
     if (FirstMd5Hash == SecondMd5Hash)
     {
        listBox1.Items.Add("the hashes are match . MD5 = " + FirstMd5Hash);
     }
     else
     {
        listBox1.Items.Add("failed with oid = " + oid);
        tbFileBack.Text = ReturnedFileName;
     }
 }

怎么了?

c# .net database postgresql npgsql
2个回答
1
投票

好吧,我已经找到了问题,事实证明(除了在帐户中采取Emil答案)你必须异步读取然后同步写入。我不知道为什么会这样。这段代码工作:

using (FileStream LocalStream = new FileStream(TempFileName, FileMode.Create))
            {
               using (var transaction = Conn.BeginTransaction())
               {
                  // create a Large Object Manager for this connection
                  var DbLargeObjectManager = new NpgsqlLargeObjectManager(Conn);

                  using (var DbStream = await DbLargeObjectManager.OpenReadAsync(oid))
                  {
                     byte[] buffer = new byte[262144]; //256KB
                     // query the database stream length
                     long DatabaseStreamLength = DbStream.Length;
                     while (DbStream.Position < DatabaseStreamLength)
                     {
                        // read from the database to buffer (async)
                        int bufferByteCount = await DbStream.ReadAsync(buffer, 0, buffer.Length);
                        //write from buffer to local file (sync)
                        LocalStream.Write(buffer, 0, bufferByteCount);
                     }
                  }
                  transaction.Commit();

0
投票

当您执行await <stream>.WriteAsync(buffer, 0, buffer.Length);时,您应该写入前一个读取方法实际读取的字节数(将返回该值)。

© www.soinside.com 2019 - 2024. All rights reserved.