续传信息持久化
增加redis接口
bool RedisMgr::SetFileInfo(const std::string& md5, std::shared_ptr<FileInfo> file_info){Json::Reader reader;Json::Value root;root["file_path_str"] = file_info->_file_path_str;root["name"] = file_info->_name;root["seq"] = file_info->_seq;root["total_size"] = file_info->_total_size;root["trans_size"] = file_info->_trans_size;auto file_info_str = root.toStyledString();auto redis_key = "file_upload_" + md5;bool success = SetExp(redis_key, file_info_str, 3600);return success;}
新增超时设置
bool RedisMgr::SetExp(const std::string& key, const std::string& value, int expire_seconds) {//执行redis命令行auto connect = _con_pool->getConnection();if (connect == nullptr) {return false;}auto reply = (redisReply*)redisCommand(connect, "SETEX %s %d %s", key.c_str(),expire_seconds,value.c_str());if (NULL == reply) {std::cout << "Execute command [ SETEX " << key << " " << expire_seconds<< " " << value << " ] failure ! " << std::endl;_con_pool->returnConnection(connect);return false;}if (!(reply->type == REDIS_REPLY_STATUS &&(strcmp(reply->str, "OK") == 0 || strcmp(reply->str, "ok") == 0))) {std::cout << "Execute command [ SETEX " << key << " " << expire_seconds<< " " << value << " ] failure ! " << std::endl;freeReplyObject(reply);_con_pool->returnConnection(connect);return false;}freeReplyObject(reply);std::cout << "Execute command [ SETEX " << key << " " << expire_seconds<< " " << value << " ] success ! " << std::endl;_con_pool->returnConnection(connect);return true;}
每次收到上传信息后,更新上传进度到redis中
_fun_callbacks[ID_UPLOAD_HEAD_ICON_REQ] = [this](shared_ptr<CSession> session, const short& msg_id,const string& msg_data) {Json::Reader reader;Json::Value root;reader.parse(msg_data, root);auto md5 = root["md5"].asString();auto seq = root["seq"].asInt();auto name = root["name"].asString();auto total_size = root["total_size"].asInt();auto trans_size = root["trans_size"].asInt();auto last = root["last"].asInt();auto file_data = root["data"].asString();auto uid = root["uid"].asInt();auto token = root["token"].asString();auto last_seq = root["last_seq"].asInt();//转化为字符串auto uid_str = std::to_string(uid);auto file_path = ConfigMgr::Inst().GetFileOutPath();auto file_path_str = (file_path / uid_str / name).string();Json::Value rtvalue;auto callback = [=](const Json::Value& result) {// 在异步任务完成后调用Json::Value rtvalue = result;rtvalue["error"] = ErrorCodes::Success;rtvalue["total_size"] = total_size;rtvalue["seq"] = seq;rtvalue["name"] = name;rtvalue["trans_size"] = trans_size;rtvalue["last"] = last;rtvalue["md5"] = md5;rtvalue["uid"] = uid;rtvalue["last_seq"] = last_seq;std::string return_str = rtvalue.toStyledString();session->Send(return_str, ID_UPLOAD_HEAD_ICON_RSP);};//第一个包校验一下token是否合理if (seq == 1) {//从redis获取用户token是否正确std::string uid_str = std::to_string(uid);std::string token_key = USERTOKENPREFIX + uid_str;std::string token_value = "";bool success = RedisMgr::GetInstance()->Get(token_key, token_value);if (!success) {rtvalue["error"] = ErrorCodes::UidInvalid;std::string return_str = rtvalue.toStyledString();session->Send(return_str, ID_UPLOAD_HEAD_ICON_RSP);return;}if (token_value != token) {rtvalue["error"] = ErrorCodes::TokenInvalid;std::string return_str = rtvalue.toStyledString();session->Send(return_str, ID_UPLOAD_HEAD_ICON_RSP);return;}}// 使用 std::hash 对字符串进行哈希std::hash<std::string> hash_fn;size_t hash_value = hash_fn(name); // 生成哈希值int index = hash_value % FILE_WORKER_COUNT;std::cout << "Hash value: " << hash_value << std::endl;//第一个包if (seq == 1) {//构造数据存储auto file_info = std::make_shared<FileInfo>();file_info->_file_path_str = file_path_str;file_info->_name = name;file_info->_seq = seq;file_info->_total_size = total_size;file_info->_trans_size = trans_size;//LogicSystem::GetInstance()->AddMD5File(md5, file_info);//改为用redis存储bool success = RedisMgr::GetInstance()->SetFileInfo(name, file_info);if (!success) {rtvalue["error"] = ErrorCodes::FileSaveRedisFailed;std::string return_str = rtvalue.toStyledString();session->Send(return_str, ID_UPLOAD_HEAD_ICON_RSP);return;}}else {//auto file_info = LogicSystem::GetInstance()->GetFileInfo(md5);//改为从redis中加载auto file_info = RedisMgr::GetInstance()->GetFileInfo(name);if (file_info == nullptr) {rtvalue["error"] = ErrorCodes::FileNotExists;std::string return_str = rtvalue.toStyledString();session->Send(return_str, ID_UPLOAD_HEAD_ICON_RSP);return;}file_info->_seq = seq;file_info->_trans_size = trans_size;bool success = RedisMgr::GetInstance()->SetFileInfo(name, file_info);if (!success) {rtvalue["error"] = ErrorCodes::FileSaveRedisFailed;std::string return_str = rtvalue.toStyledString();session->Send(return_str, ID_UPLOAD_HEAD_ICON_RSP);return;}}FileSystem::GetInstance()->PostMsgToQue(std::make_shared<FileTask>(session, uid, file_path_str, name, seq, total_size,trans_size, last, file_data, callback),index);};
资源url更新
因为上头像传资源后,要将资源的路径存储到mysql数据库中,所以我们新增MysqlMgr,这个直接从ChatServer拷贝一份即可。
但是要注意,添加如下函数
bool MysqlMgr::UpdateUserIcon(int uid, const std::string& icon) {return _dao.UpdateHeadInfo(uid, icon);}
Dao层面实现更新头像逻辑
bool MysqlDao::UpdateHeadInfo(int uid, const std::string& icon){auto con = pool_->getConnection();if (!con) {return false;}Defer defer([this, &con]() {pool_->returnConnection(std::move(con));});auto& conn = con->_con;try {std::string update_sql ="UPDATE user SET icon = ? WHERE uid = ?;";std::unique_ptr<sql::PreparedStatement> pstmt(conn->prepareStatement(update_sql));pstmt->setString(1, icon);pstmt->setInt64(2, uid);int affected_rows = pstmt->executeUpdate();// 检查是否有行被更新(可选)if (affected_rows == 0) {std::cerr << "No user found with uid: " << uid << std::endl;return false;}return true;}catch (sql::SQLException& e) {std::cerr << "SQLException in UpdateHeadInfo: " << e.what() << std::endl;return false;}return false;}
封装异步回调
之前我们处理文件上传是异步方式,将要保存的文件投递给消息队列,交给独立线程后台保存。我们没有等待处理完成就直接将消息回传给客户端,这么做不是很好,所以改为异步方式,简单的方式就是通过回调函数处理,或者包装一个future等待。这里考虑保留异步结构,所以还是用回调处理
struct FileTask {FileTask(std::shared_ptr<CSession> session, int uid, std::string path, std::string name,int seq, int total_size, int trans_size, int last,std::string file_data,std::function<void(const Json::Value&)> callback) :_session(session), _uid(uid),_seq(seq), _path(path), _name(name), _total_size(total_size),_trans_size(trans_size), _last(last), _file_data(file_data), _callback(callback){}~FileTask(){}std::shared_ptr<CSession> _session;int _uid;int _seq ;std::string _path;std::string _name ;int _total_size ;int _trans_size ;int _last ;std::string _file_data;std::function<void(const Json::Value&)> _callback; //添加回调函数};
改进后的处理
_fun_callbacks[ID_UPLOAD_HEAD_ICON_REQ] = [this](shared_ptr<CSession> session, const short& msg_id,const string& msg_data) {Json::Reader reader;Json::Value root;reader.parse(msg_data, root);auto md5 = root["md5"].asString();auto seq = root["seq"].asInt();auto name = root["name"].asString();auto total_size = root["total_size"].asInt();auto trans_size = root["trans_size"].asInt();auto last = root["last"].asInt();auto file_data = root["data"].asString();auto uid = root["uid"].asInt();auto token = root["token"].asString();auto last_seq = root["last_seq"].asInt();//转化为字符串auto uid_str = std::to_string(uid);auto file_path = ConfigMgr::Inst().GetFileOutPath();auto file_path_str = (file_path / uid_str / name).string();Json::Value rtvalue;auto callback = [=](const Json::Value& result) {// 在异步任务完成后调用Json::Value rtvalue = result;rtvalue["error"] = ErrorCodes::Success;rtvalue["total_size"] = total_size;rtvalue["seq"] = seq;rtvalue["name"] = name;rtvalue["trans_size"] = trans_size;rtvalue["last"] = last;rtvalue["md5"] = md5;rtvalue["uid"] = uid;rtvalue["last_seq"] = last_seq;std::string return_str = rtvalue.toStyledString();session->Send(return_str, ID_UPLOAD_HEAD_ICON_RSP);};//第一个包校验一下token是否合理if (seq == 1) {//从redis获取用户token是否正确std::string uid_str = std::to_string(uid);std::string token_key = USERTOKENPREFIX + uid_str;std::string token_value = "";bool success = RedisMgr::GetInstance()->Get(token_key, token_value);if (!success) {rtvalue["error"] = ErrorCodes::UidInvalid;std::string return_str = rtvalue.toStyledString();session->Send(return_str, ID_UPLOAD_HEAD_ICON_RSP);return;}if (token_value != token) {rtvalue["error"] = ErrorCodes::TokenInvalid;std::string return_str = rtvalue.toStyledString();session->Send(return_str, ID_UPLOAD_HEAD_ICON_RSP);return;}}// 使用 std::hash 对字符串进行哈希std::hash<std::string> hash_fn;size_t hash_value = hash_fn(name); // 生成哈希值int index = hash_value % FILE_WORKER_COUNT;std::cout << "Hash value: " << hash_value << std::endl;//第一个包if (seq == 1) {//构造数据存储auto file_info = std::make_shared<FileInfo>();file_info->_file_path_str = file_path_str;file_info->_name = name;file_info->_seq = seq;file_info->_total_size = total_size;file_info->_trans_size = trans_size;//LogicSystem::GetInstance()->AddMD5File(md5, file_info);//改为用redis存储bool success = RedisMgr::GetInstance()->SetFileInfo(name, file_info);if (!success) {rtvalue["error"] = ErrorCodes::FileSaveRedisFailed;std::string return_str = rtvalue.toStyledString();session->Send(return_str, ID_UPLOAD_HEAD_ICON_RSP);return;}}else {//auto file_info = LogicSystem::GetInstance()->GetFileInfo(md5);//改为从redis中加载auto file_info = RedisMgr::GetInstance()->GetFileInfo(name);if (file_info == nullptr) {rtvalue["error"] = ErrorCodes::FileNotExists;std::string return_str = rtvalue.toStyledString();session->Send(return_str, ID_UPLOAD_HEAD_ICON_RSP);return;}file_info->_seq = seq;file_info->_trans_size = trans_size;bool success = RedisMgr::GetInstance()->SetFileInfo(name, file_info);if (!success) {rtvalue["error"] = ErrorCodes::FileSaveRedisFailed;std::string return_str = rtvalue.toStyledString();session->Send(return_str, ID_UPLOAD_HEAD_ICON_RSP);return;}}FileSystem::GetInstance()->PostMsgToQue(std::make_shared<FileTask>(session, uid, file_path_str, name, seq, total_size,trans_size, last, file_data, callback),index);};
callback是我们封装的回调函数,投递给FileTask, 将来在后台线程处理FileTask时回调。
void FileWorker::task_callback(std::shared_ptr<FileTask> task){// 解码std::string decoded = base64_decode(task->_file_data);auto file_path_str = task->_path;auto last = task->_last;//std::cout << "file_path_str is " << file_path_str << std::endl;boost::filesystem::path file_path(file_path_str);boost::filesystem::path dir_path = file_path.parent_path();// 获取完整文件名(包含扩展名)std::string filename = file_path.filename().string();Json::Value result;// Check if directory exists, if not, create itif (!boost::filesystem::exists(dir_path)) {if (!boost::filesystem::create_directories(dir_path)) {std::cerr << "Failed to create directory: " << dir_path.string() << std::endl;result["error"] = ErrorCodes::FileNotExists;task->_callback(result);return;}}std::ofstream outfile;//第一个包if (task->_seq == 1) {// 打开文件,如果存在则清空,不存在则创建outfile.open(file_path_str, std::ios::binary | std::ios::trunc);}else {// 保存为文件outfile.open(file_path_str, std::ios::binary | std::ios::app);}if (!outfile) {std::cerr << "无法打开文件进行写入。" << std::endl;result["error"] = ErrorCodes::FileWritePermissionFailed;task->_callback(result);return ;}outfile.write(decoded.data(), decoded.size());if (!outfile) {std::cerr << "写入文件失败。" << std::endl;result["error"] = ErrorCodes::FileWritePermissionFailed;task->_callback(result);return ;}outfile.close();if (last) {std::cout << "文件已成功保存为: " << task->_name << std::endl;//更新头像MysqlMgr::GetInstance()->UpdateUserIcon(task->_uid, filename);//获取用户信息auto user_info = MysqlMgr::GetInstance()->GetUser(task->_uid);if (user_info == nullptr) {return ;}//将数据库内容写入redis缓存Json::Value redis_root;redis_root["uid"] = task->_uid;redis_root["pwd"] = user_info->pwd;redis_root["name"] = user_info->name;redis_root["email"] = user_info->email;redis_root["nick"] = user_info->nick;redis_root["desc"] = user_info->desc;redis_root["sex"] = user_info->sex;redis_root["icon"] = user_info->icon;std::string base_key = USER_BASE_INFO + std::to_string(task->_uid);RedisMgr::GetInstance()->Set(base_key, redis_root.toStyledString());}if (task->_callback) {task->_callback(result);}}
客户端上传逻辑修改
//上传头像void UserInfoPage::slot_up_load(){// 1. 让对话框也能选 *.webpQString filename = QFileDialog::getOpenFileName(this,tr("选择图片"),QString(),tr("图片文件 (*.png *.jpg *.jpeg *.bmp *.webp)"));if (filename.isEmpty())return;// 2. 直接用 QPixmap::load() 加载,无需手动区分格式QPixmap inputImage;if (!inputImage.load(filename)) {QMessageBox::critical(this,tr("错误"),tr("加载图片失败!请确认已部署 WebP 插件。"),QMessageBox::Ok);return;}QPixmap image = ImageCropperDialog::getCroppedImage(filename, 600, 400, CropperShape::CIRCLE);if (image.isNull())return;QPixmap scaledPixmap = image.scaled( ui->head_lb->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); // 将图片缩放到label的大小ui->head_lb->setPixmap(scaledPixmap); // 将缩放后的图片设置到QLabel上ui->head_lb->setScaledContents(true); // 设置QLabel自动缩放图片内容以适应大小QString storageDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);// 2. 在其下再建一个 avatars 子目录QDir dir(storageDir);if (!dir.exists("avatars")) {if (!dir.mkpath("avatars")) {qWarning() << "无法创建 avatars 目录:" << dir.filePath("avatars");QMessageBox::warning(this,tr("错误"),tr("无法创建存储目录,请检查权限或磁盘空间。"));return;}}// 3. 拼接最终的文件名 head.pngQString file_name = generateUniqueIconName();QString filePath = dir.filePath("avatars" +QString(QDir::separator()) + file_name);// 4. 保存 scaledPixmap 为 PNG(无损、最高质量)if (!scaledPixmap.save(filePath, "PNG")) {QMessageBox::warning(this,tr("保存失败"),tr("头像保存失败,请检查权限或磁盘空间。"));} else {qDebug() << "头像已保存到:" << filePath;// 以后读取直接用同一路径:storageDir/avatars/head.png}//实现头像上传QFile file(filePath);if(!file.open(QIODevice::ReadOnly)){qWarning() << "Could not open file:" << file.errorString();return;}//保存当前文件位置指针qint64 originalPos = file.pos();QCryptographicHash hash(QCryptographicHash::Md5);if (!hash.addData(&file)) {qWarning() << "Failed to read data from file:" << filePath;return ;}// 5. 转化为16进制字符串QString file_md5 = hash.result().toHex(); // 返回十六进制字符串//读取文件内容并发送QByteArray buffer;int seq = 0;//创建QFileInfo 对象auto fileInfo = std::make_shared<QFileInfo>(filePath);//获取文件名QString fileName = fileInfo->fileName();//文件名qDebug() << "文件名是: " << fileName;//获取文件大小int total_size = fileInfo->size();//最后一个发送序列int last_seq = 0;//获取最后一个发送序列if(total_size % MAX_FILE_LEN){last_seq = (total_size / MAX_FILE_LEN) +1;}else{last_seq = total_size / MAX_FILE_LEN;}// 恢复文件指针到原来的位置file.seek(originalPos);//每次读取MAX_FILE_LEN字节并发送buffer = file.read(MAX_FILE_LEN);QJsonObject jsonObj;//将文件内容转化为Base64 编码(可选)QString base64Data = buffer.toBase64();++seq;jsonObj["md5"] = file_md5;jsonObj["name"] = file_name;jsonObj["seq"] = seq;jsonObj["trans_size"] = buffer.size() + (seq - 1) * MAX_FILE_LEN;jsonObj["total_size"] = total_size;jsonObj["token"] = UserMgr::GetInstance()->GetToken();jsonObj["uid"] = UserMgr::GetInstance()->GetUid();if (buffer.size() + (seq - 1) * MAX_FILE_LEN == total_size) {jsonObj["last"] = 1;} else {jsonObj["last"] = 0;}jsonObj["data"] = base64Data;jsonObj["last_seq"] = last_seq;QJsonDocument doc(jsonObj);auto send_data = doc.toJson();//将md5信息和文件信息关联存储UserMgr::GetInstance()->AddNameFile(file_name, fileInfo);//发送消息FileTcpMgr::GetInstance()->SendData(ID_UPLOAD_HEAD_ICON_REQ, send_data);file.close();}
客户端加载头像
服务器将上传的头像信息保存为url更新到mysql中,接下来客户端登录需要加载新的头像
在ChatDialog的构造函数中将头像加载逻辑修改为
//模拟加载自己头像QString head_icon = UserMgr::GetInstance()->GetIcon();//使用正则表达式检查是否使用默认头像QRegularExpression regex("^:/res/head_(\\d+)\\.jpg$");QRegularExpressionMatch match = regex.match(head_icon);if (match.hasMatch()) {// 如果是默认头像(:/res/head_X.jpg 格式)QPixmap pixmap(head_icon); // 加载默认头像图片QPixmap scaledPixmap = pixmap.scaled(ui->side_head_lb->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);ui->side_head_lb->setPixmap(scaledPixmap); // 将缩放后的图片设置到QLabel上ui->side_head_lb->setScaledContents(true); // 设置QLabel自动缩放图片内容以适应大小}else {// 如果是用户上传的头像,获取存储目录QString storageDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);QDir avatarsDir(storageDir + "/avatars");// 确保目录存在if (avatarsDir.exists()) {QString avatarPath = avatarsDir.filePath(QFileInfo(head_icon).fileName()); // 获取上传头像的完整路径QPixmap pixmap(avatarPath); // 加载上传的头像图片if (!pixmap.isNull()) {QPixmap scaledPixmap = pixmap.scaled(ui->side_head_lb->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);ui->side_head_lb->setPixmap(scaledPixmap);ui->side_head_lb->setScaledContents(true);}else {qWarning() << "无法加载上传的头像:" << avatarPath;}}else {qWarning() << "头像存储目录不存在:" << avatarsDir.path();}}
聊天页面也需要修改头像加载逻辑
void ChatPage::AppendChatMsg(std::shared_ptr<ChatDataBase> msg){auto self_info = UserMgr::GetInstance()->GetUserInfo();ChatRole role;if (msg->GetSendUid() == self_info->_uid) {role = ChatRole::Self;ChatItemBase* pChatItem = new ChatItemBase(role);pChatItem->setUserName(self_info->_name);// 使用正则表达式检查是否是默认头像QRegularExpression regex("^:/res/head_(\\d+)\\.jpg$");QRegularExpressionMatch match = regex.match(self_info->_icon);if (match.hasMatch()) {pChatItem->setUserIcon(QPixmap(self_info->_icon));}else {// 如果是用户上传的头像,获取存储目录QString storageDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);QDir avatarsDir(storageDir + "/avatars");// 确保目录存在if (avatarsDir.exists()) {QString avatarPath = avatarsDir.filePath(QFileInfo(self_info->_icon).fileName()); // 获取上传头像的完整路径QPixmap pixmap(avatarPath); // 加载上传的头像图片if (!pixmap.isNull()) {pChatItem->setUserIcon(pixmap);}else {qWarning() << "无法加载上传的头像:" << avatarPath;}}else {qWarning() << "头像存储目录不存在:" << avatarsDir.path();}}QWidget* pBubble = nullptr;if (msg->GetMsgType() == ChatMsgType::TEXT) {pBubble = new TextBubble(role, msg->GetMsgContent());}pChatItem->setWidget(pBubble);auto status = msg->GetStatus();pChatItem->setStatus(status);ui->chat_data_list->appendChatItem(pChatItem);if (status == 0) {_unrsp_item_map[msg->GetUniqueId()] = pChatItem;}}else {role = ChatRole::Other;ChatItemBase* pChatItem = new ChatItemBase(role);auto friend_info = UserMgr::GetInstance()->GetFriendById(msg->GetSendUid());if (friend_info == nullptr) {return;}pChatItem->setUserName(friend_info->_name);// 使用正则表达式检查是否是默认头像QRegularExpression regex("^:/res/head_(\\d+)\\.jpg$");QRegularExpressionMatch match = regex.match(friend_info->_icon);if (match.hasMatch()) {pChatItem->setUserIcon(QPixmap(friend_info->_icon));}else {// 如果是用户上传的头像,获取存储目录QString storageDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);QDir avatarsDir(storageDir + "/avatars");// 确保目录存在if (avatarsDir.exists()) {QString avatarPath = avatarsDir.filePath(QFileInfo(friend_info->_icon).fileName()); // 获取上传头像的完整路径QPixmap pixmap(avatarPath); // 加载上传的头像图片if (!pixmap.isNull()) {pChatItem->setUserIcon(pixmap);}else {qWarning() << "无法加载上传的头像:" << avatarPath;}}else {qWarning() << "头像存储目录不存在:" << avatarsDir.path();}}QWidget* pBubble = nullptr;if (msg->GetMsgType() == ChatMsgType::TEXT) {pBubble = new TextBubble(role, msg->GetMsgContent());}pChatItem->setWidget(pBubble);auto status = msg->GetStatus();pChatItem->setStatus(status);ui->chat_data_list->appendChatItem(pChatItem);if (status == 0) {_unrsp_item_map[msg->GetUniqueId()] = pChatItem;}}}
聊天列表中加载头像逻辑修改一下
void ChatUserWid::SetChatData(std::shared_ptr<ChatThreadData> chat_data) {_chat_data = chat_data;auto other_id = _chat_data->GetOtherId();auto other_info = UserMgr::GetInstance()->GetFriendById(other_id);// 加载图片QString head_icon = UserMgr::GetInstance()->GetIcon();// 使用正则表达式检查是否是默认头像QRegularExpression regex("^:/res/head_(\\d+)\\.jpg$");QRegularExpressionMatch match = regex.match(other_info->_icon);if (match.hasMatch()) {// 如果是默认头像(:/res/head_X.jpg 格式)QPixmap pixmap(other_info->_icon); // 加载默认头像图片QPixmap scaledPixmap = pixmap.scaled(ui->icon_lb->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);ui->icon_lb->setPixmap(scaledPixmap); // 将缩放后的图片设置到QLabel上ui->icon_lb->setScaledContents(true); // 设置QLabel自动缩放图片内容以适应大小}else {// 如果是用户上传的头像,获取存储目录QString storageDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);QDir avatarsDir(storageDir + "/avatars");// 确保目录存在if (avatarsDir.exists()) {QString avatarPath = avatarsDir.filePath(QFileInfo(other_info->_icon).fileName()); // 获取上传头像的完整路径QPixmap pixmap(avatarPath); // 加载上传的头像图片if (!pixmap.isNull()) {QPixmap scaledPixmap = pixmap.scaled(ui->icon_lb->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);ui->icon_lb->setPixmap(scaledPixmap);ui->icon_lb->setScaledContents(true);}else {qWarning() << "无法加载上传的头像:" << avatarPath;}}else {qWarning() << "头像存储目录不存在:" << avatarsDir.path();}}ui->user_name_lb->setText(other_info->_name);ui->user_chat_lb->setText(chat_data->GetLastMsg());}
用户信息加载头像
UserInfoPage::UserInfoPage(QWidget *parent) :QWidget(parent),ui(new Ui::UserInfoPage){ui->setupUi(this);auto icon = UserMgr::GetInstance()->GetIcon();qDebug() << "icon is " << icon ;//使用正则表达式检查是否使用默认头像QRegularExpression regex("^:/res/head_(\\d+)\\.jpg$");QRegularExpressionMatch match = regex.match(icon);if (match.hasMatch()) {QPixmap pixmap(icon);QPixmap scaledPixmap = pixmap.scaled(ui->head_lb->size(),Qt::KeepAspectRatio, Qt::SmoothTransformation); // 将图片缩放到label的大小ui->head_lb->setPixmap(scaledPixmap); // 将缩放后的图片设置到QLabel上ui->head_lb->setScaledContents(true); // 设置QLabel自动缩放图片内容以适应大小}else {// 如果是用户上传的头像,获取存储目录QString storageDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);QDir avatarsDir(storageDir + "/avatars");// 确保目录存在if (avatarsDir.exists()) {QString avatarPath = avatarsDir.filePath(QFileInfo(icon).fileName()); // 获取上传头像的完整路径QPixmap pixmap(avatarPath); // 加载上传的头像图片if (!pixmap.isNull()) {QPixmap scaledPixmap = pixmap.scaled(ui->head_lb->size(),Qt::KeepAspectRatio, Qt::SmoothTransformation); // 将图片缩放到label的大小ui->head_lb->setPixmap(scaledPixmap); // 将缩放后的图片设置到QLabel上ui->head_lb->setScaledContents(true); // 设置QLabel自动缩放图片内容以适应大小}else {qWarning() << "无法加载上传的头像:" << avatarPath;}}else {qWarning() << "头像存储目录不存在:" << avatarsDir.path();}}//获取nickauto nick = UserMgr::GetInstance()->GetNick();//获取nameauto name = UserMgr::GetInstance()->GetName();//描述auto desc = UserMgr::GetInstance()->GetDesc();ui->nick_ed->setText(nick);ui->name_ed->setText(name);ui->desc_ed->setText(desc);//连接上connect(ui->up_btn, &QPushButton::clicked, this, &UserInfoPage::slot_up_load);}
测试效果
