PostgreSQL 使用 Bytea 二进制数据和 C libpq 保存和选择文件
PostgreSQL Save and Pick files using Bytea binary data with C libpq
这是我使用 bytea 与 postgreSQL 和 libpq 进行实验的结果。我使用接收到的数据创建的结果文件的大小是原来上传的两倍加上 2 个字节 (picture.png)。我无法确切地了解我做错了多少操作,因为这个过程对我的小动物大脑来说非常混乱。任何帮助或建议对我来说都是一个很大的帮助,在此先感谢您。
<2021 年 5 月 27 日编辑>您可以在下方找到解决方案
#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, 0, 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,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(::text, ::text, ::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, 0);
bytesFromDB = PQgetvalue(res, 0, 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;
}
我觉得我很接近,我错过了一些关于数据类型的东西 treatment/conversion。我认为这有助于理解正在发生的事情。仍在努力。我在下图中只展示了观察到的数据的一小部分。
查询的结果好像是一个表示十六进制的字符串。事实上,我将输出文件写成二进制文件,它再次将图形化的 asci 十六进制表示形式转换为真正的十六进制形式,破坏了文件的实际内容并增加了大小。我刚刚添加了关于结果文件内容发生了什么的直观解释。
这是代码运行良好的结果。我现在有时间 post 它以防它能帮助到别人。理解 bytea 的工作原理对我来说有点困难,但终于解决了。向社区致以诚挚的问候。
#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, 0, 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,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(::text, ::text, ::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, 0);
bytesFromDB = PQgetvalue(res, 0, 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;
}
这是我使用 bytea 与 postgreSQL 和 libpq 进行实验的结果。我使用接收到的数据创建的结果文件的大小是原来上传的两倍加上 2 个字节 (picture.png)。我无法确切地了解我做错了多少操作,因为这个过程对我的小动物大脑来说非常混乱。任何帮助或建议对我来说都是一个很大的帮助,在此先感谢您。
<2021 年 5 月 27 日编辑>您可以在下方找到解决方案
#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, 0, 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,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(::text, ::text, ::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, 0);
bytesFromDB = PQgetvalue(res, 0, 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;
}
我觉得我很接近,我错过了一些关于数据类型的东西 treatment/conversion。我认为这有助于理解正在发生的事情。仍在努力。我在下图中只展示了观察到的数据的一小部分。
查询的结果好像是一个表示十六进制的字符串。事实上,我将输出文件写成二进制文件,它再次将图形化的 asci 十六进制表示形式转换为真正的十六进制形式,破坏了文件的实际内容并增加了大小。我刚刚添加了关于结果文件内容发生了什么的直观解释。
这是代码运行良好的结果。我现在有时间 post 它以防它能帮助到别人。理解 bytea 的工作原理对我来说有点困难,但终于解决了。向社区致以诚挚的问候。
#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, 0, 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,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(::text, ::text, ::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, 0);
bytesFromDB = PQgetvalue(res, 0, 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;
}