PostgreSQL 使用 Bytea 二进制数据和 C libpq 保存和选取文件

问题描述

这是我用 bytea 和 postgresql 和 libpq 进行实验的结果。我使用接收到的数据创建的结果文件是原始上传文件的两倍大小加上 2 个字节 (picture.png)。我无法确切了解我做错了多少操作,因为这个过程对我的小动物大脑来说非常混乱。任何帮助或建议对我来说都是一个很大的帮助,在此先感谢您。

你可以在下面找到解决方

#include <string>
#include <fstream>
#include <iostream>

#include "libpq/libpq-fs.h"
#include "libpq-fe.h"

#pragma comment(lib,"libpq.lib")   /*!< Only for windows compilation */

int main(int argc,char* argv[])
{
    //************************ SAVING FILE TO DB AS BYTEA **********
    manager.conn = manager.ConnectDB();  // my manager,working fine    

    FILE* file = fopen("powerup.png","rb");
    if (file == NULL) cout << endl << "FILE WAS UNABLE TO BE READED" << endl;
    fseek(file,SEEK_END);
    long int fileSize = ftell(file);

    rewind(file);

    unsigned char* buffRead = (unsigned char*)malloc(fileSize);
    size_t bytes_read = fread(buffRead,1,fileSize,file);
    if (bytes_read != fileSize) cout << endl << "fread reading Error";
    fclose(file);

    const char* paramValues[3];
    paramValues[0] = "1";
    paramValues[1] = "powerup.png";
    paramValues[2] = reinterpret_cast<const char*>(buffRead);       //._.' type cast required to PQexeParams?

    const int paramFormats[3]{ 0,1 };
    const int paramLenghts[3]{ strlen(paramValues[0]),strlen(paramValues[1]),fileSize};

    PGresult *res = PQexecParams(manager.conn,"INSERT INTO filebyte (id,filename,file) VALUES($1::text,$2::text,$3::bytea)",3,/* params */
        NULL,/* let the backend deduce param type */
        paramValues,paramLenghts,/* don't need param lengths since text */
        paramFormats,/* default to all text params */
        1);

    if (res && PQresultStatus(res) == PGRES_COMMAND_OK) cout << endl << "Inserted data to filebyte OK";

    PQfreemem(res);

    //********************** PICKING FILE FROM DB AS BYTEA **********
    
    const char* bytesFromDB = new const char[fileSize];
    int sizeR = -1;
    const char *sentenceEx = "SELECT file FROM filebyte WHERE id='1'";

    res = PQexec(manager.conn,sentenceEx);

    if (res && PQresultStatus(res) == PGRES_TUPLES_OK) 
    {
        sizeR = PQgetlength(res,0);
        bytesFromDB = PQgetvalue(res,0);
    }

    ofstream myFile("picture.png",ios::out | ios::binary);

    myFile.write(bytesFromDB,sizeR);
    myFile.close();

    PQfreemem(res);
    free(buffRead);
    delete sizeP;

    manager.CloseConn(manager.conn);

    return true;
}

解决方法

这是代码正常工作的结果。我现在有时间发布它,以防它可以帮助某人。我有点难以理解 bytea 的工作原理,但最终解决了。向社区致以亲切的问候。

enter image description here

#include <string>
#include <fstream>
#include <iostream>
#include <sstream>

#include "libpq/libpq-fs.h"
#include "libpq-fe.h"

#pragma comment(lib,"libpq.lib")   /*!< Only for windows compilation */

int main(int argc,char* argv[])
{
    ManageDB manager;       

    manager.conn = manager.ConnectDB();  // My manager,connects ok

    const char* fileName = {"test.png"};
    const char* fileNameOut = { "testOut.png" };

    FILE* file = fopen(fileName,"rb");
    if (file == NULL) cout << endl << "FILE WAS UNABLE TO BE READED" << endl;
    fseek(file,SEEK_END);
    long int fileSize = ftell(file);

    rewind(file);

    unsigned char* buffRead = (unsigned char*)malloc(fileSize);
    size_t bytes_read = fread(buffRead,1,fileSize,file);
    if (bytes_read != fileSize) cout << endl << "fread reading Error";
    fclose(file);
    
    size_t* sizeP = new size_t(fileSize);

    const char* paramValues[3];
    paramValues[0] = "1";
    paramValues[1] = fileName;
    paramValues[2] = reinterpret_cast<const char*>(buffRead);   // type cast required to PQexeParams

    const int paramFormats[3]{ 0,1 };   //0 = text,1 = binary
    const int paramLenghts[3]{ strlen(paramValues[0]),strlen(paramValues[1]),fileSize};

    PGresult *res = PQexecParams(manager.conn,"INSERT INTO filebyte (id,filename,file) VALUES($1::text,$2::text,$3::bytea)",3,/* params num */
        NULL,/* let the backend deduce param type */
        paramValues,paramLenghts,paramFormats,1);

    if (res && PQresultStatus(res) == PGRES_COMMAND_OK) cout << endl << "Inserted data to filebyte OK";

    PQfreemem(res);

    //************************download from DB*************************
    
    const char* bytesFromDB = new const char[fileSize];
    int sizeR;
    // Table columns = id(text) | filename(text) | file(bytea)
    const char* sentenceEx = "SELECT file FROM filebyte WHERE id='1'";

    res = PQexec(manager.conn,sentenceEx);

    if (res && PQresultStatus(res) == PGRES_TUPLES_OK)
    {
        sizeR = PQgetlength(res,0);
        bytesFromDB = PQgetvalue(res,0);
    }
    else cout << endl << "Error at inserting file data in filebyte table";

    string hex(bytesFromDB,2,sizeR-2);  //removing the first '\x' characters of the result.

    // vars for converting to real bytes process
    std::basic_string<uint8_t> bytes;
    bytes.clear();
    std::string nextbyte; 
    nextbyte.clear();
    uint16_t byte;

    // Iterate over every pair of hex values in the input string (e.g. "18","0f",...)
    for (size_t i = 0; i < hex.length(); i += 2)
    {
        // Get current pair and store in nextbyte
        nextbyte = hex.substr(i,2);

        // Put the pair into an istringstream and stream it through std::hex for
        // conversion into an integer value.
        // This will calculate the byte value of your string-represented hex value.
        std::istringstream(nextbyte) >> std::hex >> byte;

        // As the stream above does not work with uint8 directly,we have to cast it now.
        // As every pair can have a maximum value of "ff",// which is "11111111" (8 bits),we will not lose any information during this cast.
        bytes.push_back(static_cast<uint8_t>(byte));
    }

    // string obj from bytes-"array"
    std::string result(begin(bytes),end(bytes));

    ofstream myFile(fileNameOut,ios::out | ios::ios_base::binary);
    
    if (myFile.is_open())
    {
        myFile << result;
        myFile.close();
    }
    else cout << endl << "Impossible to writte the file " << fileNameOut;
    
    manager.CloseConn(manager.conn);   //closes connection with database internally ok

    PQfreemem(res);
    free(buffRead);
    delete sizeP;
    free((char*)fileName[8]);
    free((char*)fileNameOut[11]);
    
    return true;
}

,

我觉得我很接近,我对数据类型处理/转换有一些误解。我认为这有助于了解正在发生的事情。仍在努力。我在下图中只展示了观察数据的一小部分。

Data Analisys with VSCode binary viewer

查询的结果好像是一个十六进制的字符串。事实上,我将输出文件写为二进制文件,它再次将具象的 asci 十六进制表示转换为真正的十六进制,破坏了文件的真实内容并增加了大小。我只是添加了一个关于结果文件内容发生了什么的视觉解释。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...