杨记

碎片化学习令人焦虑,系统化学习使人进步

0%

mongocxx安装和使用

https://www.cnblogs.com/mxnote/p/17098385.html

mongodb C驱动安装 mongodb C++驱动安装

mongo-cxx-driver github源码地址

mongodb的API文档

mongodb安装

官方教程:https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-red-hat/

CentOS 7 yum安装MongoDB - 斌言 - 博客园 (cnblogs.com)

1、配置MongoDB的yum源(这里使用阿里云的源),创建文件/etc/yum.repos.d/mongodb-org-6.0.repo,并输入以下内容

1
2
3
4
5
[mngodb-org]
name=MongoDB Repository
baseurl=http://mirrors.aliyun.com/mongodb/yum/redhat/$releasever/mongodb-org/6.0/x86_64/
gpgcheck=0
enabled=1
1
2
3
4
$ yum update -y   # 更新yum
$ yum install mongodb-org -y # 下载MongoDB
$ rpm -qa | grep mongodb # 查看安装的mongodb组件
$ systemctl start mongod # 开启mongodb服务

C++驱动安装

https://www.mongodb.com/docs/drivers/cxx/#mongodb-compatibility

https://mongocxx.org/mongocxx-v3/installation/

安装mongocxx 3.8,对应mongodb 6.0

安装过程中可能会因为缺少依赖而报错,只要根据错误信息安装相关依赖即可

官方标注的前提条件

  • Any standard Unix platform, or Windows 7 SP1+
  • A compiler that supports C++11 (gcc, clang, or Visual Studio)
  • CMake 3.2 or later
  • boost headers (optional)

安装Cmake

根据实际需要安装对应的Cmake

1
2
3
4
5
6
7
$ wget https://github.com/Kitware/CMake/releases/download/v3.27.1/cmake-3.27.1.tar.gz
$ tar zxvf cmake-3.27.1.tar.gz
$ cd cmake-3.27.1
$ ./bootstrap
$ make
$ make install
$ cmake --version # 查看版本

安装mongoc

详细过程和解释参照官网:http://mongoc.org/libmongoc/current/installing.html

1
2
3
4
5
6
7
8
9
$ yum install mongo-c-driver-devel
$ yum install libbson-devel
$ yum install openssl-devel cyrus-sasl-devel libbsd-devel libzstd-devel bind-utils libicu-devel
$ wget https://github.com/mongodb/mongo-c-driver/releases/download/1.24.2/mongo-c-driver-1.24.2.tar.gz
$ tar xzf mongo-c-driver-1.24.2.tar.gz
$ cd mongo-c-driver-1.24.2
$ mkdir cmake-build
$ cd cmake-build
$ cmake -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF ..

cmake成功显示如下:

1
-- Build files have been written to: /home/user/mongo-c-driver-1.24.2/cmake-build

问题1-- Looking for ASN1_STRING_get0_data in /usr/lib64/libcrypto.so - not found

  • 当openssl <= 1.0.1时,ASN1_STRING_get0_data该方法不存在,作为替代是ASN1_STRING_data方法
  • 当openssl > 1.0.1时,ASN1_STRING_get0_data方法存在,但实际出问题的时候,服务器版本是Open SSL1.0.2.k,建议安装之后的版本

参考:https://www.bbsmax.com/A/xl56YnWxJr/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 安装1.1.0f版本
$ wget http://www.openssl.org/source/openssl-1.1.0f.tar.gz
$ tar zxvf openssl-1.1.0f.tar.gz
$ cd openssl-1.1.0f
$ ./config --prefix=/usr/local/ssl
$ make && make install
# 将旧的openssl执行文件及目录重命令
$ mv -f /usr/bin/openssl /usr/bin/openssl.old
$ mv -f /usr/include/openssl /usr/include/openssl.old
# 修改软链,指向新版本的openssl路径
$ ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl
$ ln -s /usr/local/ssl/include/openssl /usr/include/openssl
# 将指向libcrypto.so.1.0.2k的软链接删除,创建指向libcrypto.so.1.1的软链接
$ rm /usr/lib64/libcrypto.so
$ ln -s /usr/local/ssl/lib/libcrypto.so.1.1 /usr/lib64/libcrypto.so
# 查看是否有ASN1_STRING_get0_data方法
$ nm /usr/lib64/libcrypto.so | grep ASN1_STRING_get0_data
0000000000088f20 T ASN1_STRING_get0_data
1
2
$ cmake --build .
$ cmake --build . --target install

测试mongoc是否安装成功:

1
2
3
4
5
6
7
8
#include <mongoc/mongoc.h>
#include <stdio.h>
int main() {
mongoc_init();
printf("libmongoc version: %s\n", mongoc_get_version());
mongoc_cleanup();
return 0;
}
1
2
3
$ gcc test_mongoc.c -I/usr/local/include/libmongoc-1.0 -I/usr/local/include/libbson-1.0 -L/usr/local/lib64 -lmongoc-1.0 -lbson-1.0
$ ./a.out
libmongoc version: 1.3.6

安装boost库

1、使用yum安装,版本可能不合适(不推荐)

1
$ yum install boost boost-devel boost-doc

2、源码安装(国内下载巨慢,建议魔法上网)

去官网选择对应版本下载:https://www.boost.org/users/history/ ,我下载的为1.70.0版本:https://www.boost.org/users/history/version_1_70_0.html

注意:

安装boost库的指令流程,下载对应版本的压缩包后:

1
2
3
4
5
6
7
$ tar zxvf boost_1_70_0.tar.gz 
$ cd boost_1_70_0
$ yum install bzip2 bzip2-devel xz xz-devel
$ ./bootstrap.sh --prefix=/usr/local/boost # 指定安装目录,需要自己创建
$ ./b2
The Boost C++ Libraries were successfully built!
$ ./b2 install

安装过程中可能会出一些问题,根据提示信息处理,示例如下:

1)缺少Boost库的Unicode/ICU支持,安装ICU库yum install -y libicu-devel

image-20230730105731103

2)

image-20230730110116673

/etc/ld.so.conf文件中追加:/usr/local/boost/lib

测试是否安装成功:g++ -o test test.cpp -I /usr/local/boost/include/

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <boost/algorithm/string.hpp>

int main()
{
std::string str = "hello, boost library!";
boost::algorithm::to_upper(str);
std::cout << str << std::endl;
return 0;
}

安装mongocxx

注意GCC的版本要支持C++17,建议使用GCC7以后的版本

1
2
3
4
5
6
7
$ wget https://github.com/mongodb/mongo-cxx-driver/releases/download/r3.8.0/mongo-cxx-driver-r3.8.0.tar.gz
$ tar -xzf mongo-cxx-driver-r3.8.0.tar.gz
$ cd mongo-cxx-driver-r3.8.0/build
$ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local
$ cmake --build . --target EP_mnmlstc_core
$ cmake --build .
$ cmake --build . --target install
1
2
3
4
5
6
$ echo '/usr/local/lib64' >> /etc/ld.so.conf   # 增加libmongocxx.so._noabi的链接路径
$ find /usr -name libmongocxx*.pc # 查找libmongocxx.pc所在位置
/usr/local/lib64/pkgconfig/libmongocxx.pc
$ export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig # 设置pkg-config查找pc文件路径
$ pkg-config --cflags --libs libmongocxx #
-I/usr/local/include/mongocxx/v_noabi -I/usr/local/include/bsoncxx/v_noabi -L/usr/local/lib64 -lmongocxx -lbsoncxx

mongodb测试

g++ -o testmongo testmongo.cpp $(pkg-config --cflags --libs libmongocxx)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <memory>
#include <cstring>

#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/exception/exception.hpp>
#include <bsoncxx/builder/stream/document.hpp>
#include <bsoncxx/json.hpp>

using namespace std;
using bsoncxx::builder::stream::document;
using bsoncxx::builder::stream::finalize;

void printIfAge(mongocxx::client& client, int age) {
auto collection = client["tutorial"]["persons"];
auto cursor = collection.find(document{} << "age" << age << finalize);
for (auto&& doc : cursor) {
cout << doc["name"].get_utf8().value.to_string() << endl;
}
}

void run(const char* dbhost) {
mongocxx::instance instance{}; // Required to initialize the driver
mongocxx::client client{mongocxx::uri{dbhost}};
cout << "connected ok" << endl;
auto collection = client["tutorial"]["persons"];
collection.insert_one(document{} << "name" << "Joe" << "age" << 33 << finalize);
collection.insert_one(document{} << "name" << "Jane" << "age" << 40 << finalize);
collection.insert_one(document{} << "name" << "Abe" << "age" << 33 << finalize);
collection.insert_one(document{} << "name" << "Samantha" << "age" << 21 << "city" << "Los Angeles" << "state" << "CA" << finalize);
collection.create_index(document{} << "age" << 1 << finalize);
cout << "count: " << collection.count_documents({}) << endl;
auto cursor = collection.find({});
for (auto&& doc : cursor) {
cout << bsoncxx::to_json(doc) << endl;
}
cout << "\nprintifage:\n";
printIfAge(client, 33);
}

int main(int argc, char *argv[]) {
const char* dbhost = "mongodb://localhost:27017";
if (argc == 2) {
dbhost = argv[1];
printf("connect to dbhost:[%s]\n", dbhost);
} else {
printf("connect to dbhost:[%s]\n", dbhost);
printf("if you need to connect to a remote service, please input IP address!\n");
}
try {
run(dbhost);
} catch (const mongocxx::exception& e) {
cout << "caught " << e.what() << endl;
}
return 0;
}

GirdFS示例

源自黑马教程

需求总体描述:开发类似于网盘功能,支持文件(图片,视频,mp3等)的上传和下载.mongodb的优势

功能需求:支持多用户

  1. 用户的校验,必须是注册用户,用户名和密码正确才能上传和下载.(注册部分正常需要web前端开发,该案列不做注册部分功能,直接在mysql的用户表增加记录即可).
  2. 查看登陆用户上传的文件信息.(支持命令行的方式查看即可,不需要做web前端),实现管理台.
  3. 上传文件
  4. 下载上传过的文件到本地.
  5. 删除上传的文件.(可根据情况是否添加,不是必须)

技术实现要求:Mysql+mongo

  1. 用mysql存储用户的信息,文件上传和下载的对应关系(本地和mongo中文件的对应关系)
  2. Mongo存放文件.

MySQL建库建表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
create database ods character set utf8;

use ods;

// 用户信息表:用户id,用户姓名,密码,手机号,公司,邮箱,备注
create table t_user_info(
user_id varchar(30),
user_name varchar(50),
pass_word varchar(16),
remark varchar(100)
);

// 文件信息表:本地文件名,文件类型,大小,上传时间,修改时间,下载次数,用户id,mongo文件名,备注
create table t_file_info(
file_id int primary key auto_increment,
local_file_name varchar(50),
mongo_file_name varchar(50),
file_size int,
upload_date timestamp,
user_id varchar(30),
remark varchar(100)
);


insert into t_user_info values('yekai','叶开','123','bianchenglangzi');
insert into t_user_info values('fuhongxue','傅红雪','456','bianchenglangzi');

// 在文件信息表中增加上传状态:上传中,上传成功
alter table t_file_info add column status varchar(2);

实现:

  1. CMysql.hCMysql.cpp实现和mysql的交互
  2. CMongo.hCMongo.cpp实现和mongodb的交互
  3. main.cpp控制台,和用户交互

操作指令:

  • 上传 upload srcfile mongofile
  • 下载 download mongofile desfile
  • 查看 list
  • 退出 quit
  • 删除 delete mongofile localfile

代码

1
2
3
4
5
6
7
8
9
10
11
12
// CMysql.h
#pragma once
class CMysql
{
public:
CMysql();
~CMysql();
int UserLogin(const char* User, const char* Passwd);
int ListFileInfo(const char* User);
int UploadFile(const char* User, const char* LocalFile, const char* MongoFile);
int UpdateFileInfo(int FileSize, const char* User, const char* LocalFile, const char* MongoFile);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// CMysql.cpp
#include "CMysql.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql.h>

MYSQL* mysql = NULL;

CMysql::CMysql()
{
mysql = mysql_init(NULL);
if (mysql == NULL)
{
printf("mysql init err\n");
exit(1);
}
mysql = mysql_real_connect(mysql, "localhost", "root", "123456", "ods", 0, NULL, 0);
if (mysql == NULL)
{
printf("mysql connect err\n");
exit(1);
}
}

CMysql::~CMysql()
{
if (mysql)
{
mysql_close(mysql);
}
}

//用户登录
int CMysql::UserLogin(const char* User, const char* Passwd)
{
// select * from t_user_info where user_id='yekai' and pass_word='123';
if (strstr(Passwd, " or ")) // select * from t_user_info where user_id='yekai' and pass_word='0' or '1=1' sql注入
{
printf("zhe yang bu hao!\n");
return 0;
}
char rSql[512] = { 0 };
sprintf(rSql, "select user_id from t_user_info where user_id='%s' and pass_word='%s'", User, Passwd);
if (mysql_query(mysql, rSql) != 0)
{
printf("mysql exe err: %s\n", rSql);
return -1;
}
MYSQL_RES* result = mysql_store_result(mysql); // 取结果集
if (result != NULL)
{
int ret = (int)result->row_count;
mysql_free_result(result);
return ret;
}
return 0;
}

int CMysql::ListFileInfo(const char* User)
{
// select * from t_file_info where user_id='yekai';
char rSql[512] = { 0 };
sprintf(rSql, "select * from t_file_info where user_id='%s'", User);
if (mysql_query(mysql, rSql) != 0)
{
printf("mysql exe err: %s\n", rSql);
return -1;
}
MYSQL_RES* result = mysql_store_result(mysql); // 取结果集
if (result != NULL)
{
MYSQL_ROW row;
unsigned int num_fields;
unsigned int i;

num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result)))
{
unsigned long* lengths;
lengths = mysql_fetch_lengths(result);
for (i = 0; i < num_fields; i++)
{
printf("%s\t", row[i] ? row[i] : "NULL");
}
printf("\n");
}
mysql_free_result(result);
}
return 0;
}

int CMysql::UploadFile(const char* User, const char* LocalFile, const char* MongoFile)
{
// insert into t_file_info(local_file_name, mongo_file_name, file_size, user_id, status, remark)
// values('xxx.11', 'yyy.11', 1024, 'yekai', '0', 'upload success');
char rSql[512] = { 0 };
sprintf(rSql, "insert into t_file_info(local_file_name, mongo_file_name, file_size, user_id, status, remark)\
values('%s', '%s', 0, '%s', '0', 'uploading')", LocalFile, MongoFile, User);
if (mysql_query(mysql, rSql) != 0)
{
printf("run upload err: %s\n", rSql);
return -1;
}
return 0;
}

int CMysql::UpdateFileInfo(int FileSize, const char* User, const char* LocalFile, const char* MongoFile)
{
// update t_file_info set file_size=1011, status='1', remark='upload success' where user_id='yekai' and local_file_name='xxx.11' and mongo_file_name='yyy.11'
char rSql[512] = { 0 };
sprintf(rSql, "update t_file_info set file_size=%d, status='1', remark='upload success' where user_id='%s' \
and local_file_name='%s' and mongo_file_name='%s'", FileSize, User, LocalFile, MongoFile);
if (mysql_query(mysql, rSql) != 0)
{
printf("run update err: %s\n", rSql);
return -1;
}
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
// CMongo.h
#pragma once
#include <iostream>

class CMongo
{
public:
CMongo();
size_t UploadFile(const char* LocalFile, const char* MongoFile);
int DownloadFile(const char* MongoFile, const char* DesFile);
~CMongo();
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// CMongo.cpp
#include "CMongo.h"
#include <bsoncxx/builder/basic/document.hpp>
#include <bsoncxx/json.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/stdx.hpp>
#include <mongocxx/uri.hpp>
#include <fstream>

using bsoncxx::builder::basic::kvp;
using bsoncxx::builder::basic::make_array;
using bsoncxx::builder::basic::make_document;

mongocxx::instance instance{}; // Required to initialize the driver
mongocxx::client client{ mongocxx::uri{ "mongodb://localhost:27017" } };
//mongocxx::database db = client["ods"];
mongocxx::gridfs::bucket bucket = client["ods"].gridfs_bucket();

CMongo::CMongo()
{
// 检查连接是否成功
auto databases = client.list_databases();
if (databases.begin() == databases.end())
{
std::cout << "Failed to connect to MongoDB server." << std::endl;
exit(1);
}
}

CMongo::~CMongo()
{

}

size_t CMongo::UploadFile(const char* LocalFile, const char* MongoFile)
{
std::ifstream file(LocalFile, std::ios::in | std::ios::binary);
if (!file) {
throw std::runtime_error{ "unable to open file: " + std::string(LocalFile) };
}

mongocxx::gridfs::uploader uploader = bucket.open_upload_stream(MongoFile);
const int buffer_size = 40960;
char buffer[buffer_size];
size_t fileSize = 0;
while (!file.eof())
{
file.read(buffer, buffer_size);
std::streamsize bytesRead = file.gcount();
uploader.write(reinterpret_cast<uint8_t*>(buffer), bytesRead);
fileSize += (size_t)bytesRead;
}
auto result = uploader.close();
if (result.id().type() == bsoncxx::type::k_oid) {
// 文件上传成功,可以获取文件的唯一ID
auto fileId = result.id().get_oid().value.to_string();
std::cout << "文件上传成功,文件ID:" << fileId << std::endl;
}
file.close();
return fileSize;
}

int CMongo::DownloadFile(const char* MongoFile, const char* DesFile) {
bsoncxx::document::value filter = make_document(kvp("filename", MongoFile));
mongocxx::cursor cursor = bucket.find(filter.view());
if (cursor.begin() != cursor.end())
{
std::ofstream file(DesFile, std::ios::out | std::ios::binary);
bsoncxx::document::view documentView = *cursor.begin();
bsoncxx::types::bson_value::view id = documentView["_id"].get_value();
auto downloader = bucket.open_download_stream(id);
const int buffer_size = 4096;
char buffer[buffer_size];
size_t readbyte = 0;
while ((readbyte = downloader.read((uint8_t*)buffer, buffer_size)) != 0)
{
file.write(buffer, readbyte);
}
file.close();
downloader.close();
}
else
{
return -1;
}
return 0; // 返回0表示下载成功
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// main.cpp
#include<iostream>
#include<vector>
#include<string>

#include "CMysql.h"
#include "CMongo.h"

using namespace std;

void splitString(const char* Src, char delim, vector<string>& vsplit)
{
string tmp = Src;
vsplit.clear();
int index = 0;
size_t last = 0;
last = tmp.find_first_not_of(delim, last); // 找到第一个不为分隔符的字符
index = tmp.find_first_of(delim, last); //找到第一个分隔符
while (index != string::npos) // npos代表字符串的结尾
{
vsplit.push_back(tmp.substr(last, index - last));
last = tmp.find_first_not_of(delim, index);
index = tmp.find_first_of(delim, last);
}
if (index == string::npos && tmp.length() > last)//到末尾了,如果整个长度大于last坐标,说明还有最后一个字符要放到vector
{
vsplit.push_back(tmp.substr(last));
}
#if 0
cout << vsplit.size() << endl;
for (size_t i = 0; i < vsplit.size(); i++)
{
cout << "i=" << i << "," << vsplit[i].c_str() << endl;
}
#endif
}

int main(int argc, char* argv[])
{
system("stty erase ^H"); // 解决backspace键显示^H问题
if (argc != 3)
{
cout << "./main user passwd" << endl;
return -1;
}
vector<string> vCmd;
string line;
CMysql cmysql;
CMongo cmongo;
if (cmysql.UserLogin(argv[1], argv[2]) < 1)
{
cout << "login err" << endl;
return -1;
}
while (1)
{
cout << "ods> ";
getline(cin, line);
splitString(line.c_str(), ' ', vCmd);
if (vCmd.empty()) continue;
if (vCmd[0].compare("quit") == 0) // quit
{
cout << "bye bye!" << endl;
break;
}
else if (vCmd[0].compare("upload") == 0)
{
cout << "upload" << vCmd[1] << ", " << vCmd[2] << endl;
cmysql.UploadFile(argv[1], vCmd[1].c_str(), vCmd[2].c_str());
size_t size = cmongo.UploadFile(vCmd[1].c_str(), vCmd[2].c_str());
cmysql.UpdateFileInfo(size, argv[1], vCmd[1].c_str(), vCmd[2].c_str());
}
else if (vCmd[0].compare("download") == 0)
{
cout << "download" << vCmd[1] << ", " << vCmd[2] << endl;
if (cmongo.DownloadFile(vCmd[1].c_str(), vCmd[2].c_str()) != 0)
{
cout << "文件未找到" << endl;
}
}
else if (vCmd[0].compare("list") == 0)
{
// cout << "list" << endl;
cmysql.ListFileInfo(argv[1]);
}
}
return 0;
}

编译指令: 注意GCC的版本

1
g++ main.cpp CMysql.cpp CMongo.cpp -o main -I/usr/include/mysql -L/usr/lib64/mysql -lmysqlclient -ldl -lpthread -lrt -I/usr/local/include/mongocxx/v_noabi -I/usr/local/include/bsoncxx/v_noabi  -L/usr/local/lib64 -lmongocxx -lbsoncxx

image-20230805182700262

image-20230805182804046

欢迎关注我的其它发布渠道