如何使用WinApi从MS SQL表中检索大型二进制数据?

时间:2016-10-04 06:04:13

标签: c++ sql-server winapi odbc

我需要从ms sql表中放入和检索大型二进制数据。

要使用ifstream和SQLPutData放置数据。这是我放置数据的代码(没有错误检查和分配一些句柄):

 char file[MAX_PATH] = "";
    strcpy(file, argv[2]);
    ifstream f(file, ios::binary | ios::ate);
    size_t sz = f.tellg();
    f.seekg(0, ios::beg);
    char *buf = new char[sz];
    char *ptr = buf;
    f.read(ptr, sz);
    f.close();
SQLLEN lbytes, cbBinarySize;
    PTR pParamID;
    lbytes = (SDWORD) sz;
    cbBinarySize = SQL_LEN_DATA_AT_EXEC(lbytes);

char SQL[512] = "INSERT INTO bindata (filename, binData) VALUES ('";
    strcat(SQL, file);
    strcat(SQL, "', ?)");

ret = SQLPrepare(stmt, (SQLCHAR *) SQL, SQL_NTS);
SQLSMALLINT dataType, decDigits, nullable;
    SQLUINTEGER paramsize;
ret = SQLDescribeParam(stmt, 1, &dataType, &paramsize, &decDigits, &nullable);
    ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, dataType, paramsize, 0, (VOID *)1, 0, &cbBinarySize);

    ret = SQLExecDirect(stmt, (SQLCHAR *) SQL, SQL_NTS);

    if (ret != SQL_NEED_DATA && ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) return ret;

    ret = SQLParamData(stmt, &pParamID);
    if (ret == SQL_NEED_DATA) {
        while (lbytes > 5000 ) {

            ret = SQLPutData(stmt, (SQLCHAR *)ptr, 5000);
            lbytes -= 5000;
            ptr += 5000;
        }

        ret = SQLPutData(stmt, ptr, lbytes);


        ret = SQLParamData(stmt, &pParamID);
        if (ret != SQL_SUCCESS) return ret;

}
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
delete[] ptr;
delete[] buf;

这对我来说很好,我在我的VARBINARY(MAX)表字段中有二进制数据。现在我想从那里获得这些数据。这是我试图做的(我查看了MSDN文章https://msdn.microsoft.com/en-us/library/ms811006.aspx):

unsigned char* data = new unsigned char[2500000];
    SQLINTEGER cbFdata = SQL_NTS;

    SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
    ret = SQLExecDirect(stmt, (SQLCHAR *) "SELECT binData FROM bindata WHERE filename='AgentCPP.exe'", SQL_NTS);
    if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) return ret;

    ret = SQLFetch(stmt);

    do {

        ret = SQLGetData(stmt, 1, SQL_C_BINARY, data, 5000, &cbFdata);

    } while (ret != SQL_NO_DATA_FOUND);


    SQLFreeHandle(SQL_HANDLE_STMT, stmt);

但是不是包含我的数据的数组,而是一些垃圾,这可能只是我数据的一部分。我究竟做错了什么? 如果我使用.Net,我可能会使用某种byte []数组,或者其他东西,但在这里我不能。你能帮我吗?

UPD。只是尝试使用C#来做它,它花了我大约3分钟和十几行代码。只是为了显示我想要获得的结果 - 这段代码从二进制数据创建了一个完全正常工作的exe文件,该文件由我在这个问题的第一个代码块放入数据库中:

OdbcCommand com = new OdbcCommand(@"SELECT binData FROM bindata WHERE filename='AgentCPP.exe'", con);
        com.Connection = con;
        OdbcDataReader reader = com.ExecuteReader();

        if (reader.Read())
            binData =  (byte[]) reader.GetValue(0);

        reader.Dispose();
        com.Dispose();
        con.Close();

        FileStream fs = new FileStream(@"agent.exe", FileMode.Create, FileAccess.Write);
        fs.Write(binData, 0, binData.Length);
        fs.Close();

如何使用c winapi实现相同目标?

1 个答案:

答案 0 :(得分:1)

好吧,我似乎弄清了我的代码中出了什么问题。

首先,我必须检索实际的数据长度,而不仅仅是使用2500000近似大小的数组,所以最初我使用假指针来获取数据大小,然后使用std :: vector。 其次,我将5000放入SQLGetData作为我数据的“部分”,这是错误的,我需要使用实际的数据大小。所以这是代码,对我来说很好用:

SQLINTEGER cbSize = SQL_NTS;

    ret = SQLExecDirect(stmt, (SQLCHAR *) "SELECT binData FROM bindata WHERE filename='AgentCPP.exe'", SQL_NTS);
    if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) return ret;

    ret = SQLFetch(stmt);

    char *ptr = new char[1];
    if ((ret = SQLGetData(stmt, 1, SQL_C_BINARY, ptr, 0, &cbSize) != SQL_NO_DATA))
    {

        vector<char> vec(cbSize);
        SQLGetData(stmt, 1, SQL_C_BINARY, &vec[0], cbSize, &cbSize);
        ofstream fout;
        fout.open("test.exe", ios::binary);
        fout.write(&vec[0], cbSize);
        fout.close();
        vec.clear();
        }
    delete[] ptr;