阿里云提供了人脸识别的api,其示例除了c/c++,其他主流语言都有相应的实例。
本人由于项目债务和集成需要,需要用c/c++实现,若只是支持win/Linux平台,采用
acl_master源码库也可行,有需要的可参考《阿里云短信服务接口的c++实现》,
阿里云的短信服务接口与人脸识别在数据签名、加密等方面是一致的。
我当前项目由于跨平台支持android编译,所以采用了qt实现阿里云的人脸识别接口。
备注:若需要静态编译实现阿里云的人脸识别,可能 qt环境需要重新静态编译network模块,追加openssl静态编译支持,
更细节的实现可以参考《qt5.8_for_vs2015 and openssl静态编译 》。
阿里云的人脸识别主要有两个难点,其一是,若你直接传输图片内容,需要对图片内容进行编码,其二是需要实现签名认证。
下面就如何实现阿里云的人脸属性识别的过程(人脸检测定位、人脸比对类似):
1)首先需要开通 阿里云 的人脸识别服务,下面是本人在阿里云的云产品通用代金券链接:
https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=pfb80n4a
开通服务后,进入 人脸识别的控制台,在其左侧栏目有个API调试,点击进去,这里标注的
请求地址是我们需要用到的,例如:https://dtplus-cn-shanghai.data.aliyuncs.com/face/attribute
另外 Access Key ID和Access Key Secret在API调用也需要,在你的头像下的AccessKeys项点击进去获取即可。
2)阿里API说明手册指出,若图片内容指定url,需要你只身配备网络存储路径,采用阿里的OSS也是不错的选择,若直接传输图片内容,需要采用base64编码,刚好qt5.8就能直接实现,下面就直接传输本地图片内容进行阿里云人脸识别API调用,记得qt工程文件需要添加,
QT += network
示例代码:
QByteArray FaceDetectObject::Image_To_Base64(QString image_path)
{
QImage image(image_path);
QByteArray ba;
QBuffer buf(&ba);
image.save(&buf,"jpg");
QByteArray hexed = ba.toBase64();
buf.close();
return hexed;
}
3)关于人脸识别的通信接口的签名的细节要求查看官方说明文档《API校验规范》章节,其内容要求UTF-8和Base64编码,签名算法遵循RFC 2104HMAC-SHA1规范。
MD5转换示例代码:
QByteArray FaceDetectObject::getMD5(QByteArray bytes_)
{
QCryptographicHash ch(QCryptographicHash::Md5);
QByteArray ret;
ch.addData(bytes_);
ret = ch.result();
return ret;
}
HMACSha1算法示例代码:
QByteArray FaceDetectObject::HMACSha1(QByteArray key, QByteArray baseString)
{
int blockSize = 64; // HMAC-SHA-1 block size, defined in SHA-1 standard
if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with SHA-1 compression
key = QCryptographicHash::hash(key, QCryptographicHash::Sha1);
}
QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6"
QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "/"
// ascii characters 0x36 ("6") and 0x5c ("/") are selected because they have large
// Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
for (int i = 0; i < key.length(); i++) {
innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
}
// result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64
QByteArray total = outerPadding;
QByteArray part = innerPadding;
part.append(baseString);
total.append(QCryptographicHash::hash(part, QCryptographicHash::Sha1));
QByteArray hashed = QCryptographicHash::hash(total, QCryptographicHash::Sha1);
/// 注意——>把字符串hashed转换为Hex,内存中的ASCII码arrayFromHexString
QByteArray arrayFromHexString = QByteArray::fromHex(hashed.toHex());
qDebug()<<"hmacSha1内存中的ASCII码 arrayFromHexString \n"<<arrayFromHexString.toHex();
return arrayFromHexString;
}
4)阿里云一般对于http协议的时间格式要求是GMT的
也就是QT里面的UTC格式, 实现 样例:
QLocale lo = QLocale::English;//设置QLocale为英文
QString date = lo.toString(QDateTime::currentDateTimeUtc(),"ddd, dd MMM yyyy hh:mm:ss")+QString(" GMT");
其通信协议需要openssl支持才能实现https请求,实现 样例:
QNetworkRequest request;
QSslConfiguration config;
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::TlsV1_0);
request.setSslConfiguration(config);
5)实现阿里云的人脸属性识别请求,记得把key和密钥换成自己的:
void FaceDetectObject::do_post_al(QByteArray bytearray)
{
qDebug()<<"img_bytearray_size="<<bytearray.size()<<"\n";
// qDebug()<<"img_bytearray="<<bytearray.data()<<"\n";
url = QUrl("https://dtplus-cn-shanghai.data.aliyuncs.com/face/attribute",QUrl::TolerantMode);
// url.toEncoded();
qDebug() <<"url="<<url.url()<<"\n";
QString ak_id = "**************"; //you key
QString ak_secret = "**************************";//you secret
/*
* http header 参数
*/
QString method = "POST";
QString accept = "application/json";
QString content_type = "application/json";
QLocale lo = QLocale::English;//设置QLocale为英文
QString date = lo.toString(QDateTime::currentDateTimeUtc(),"ddd, dd MMM yyyy hh:mm:ss")+QString(" GMT");
qDebug() <<"date = " << date <<"\n";
//1.对body做MD5+BASE64加密
QByteArray body = QString("{\"type\":\"1\",\"content\":\"%1\"}").arg(bytearray.data()).toLocal8Bit();
qDebug() <<"body = " << body <<"\n";
QByteArray bodyMd5 = getMD5(body);
qDebug() <<"bodyMd5 = " << bodyMd5 <<"\n";
QString bodyBase64 = bodyMd5.toBase64();
qDebug() <<"bodyBase64 = " << bodyBase64 <<"\n";
QString stringToSign = method + "\n" + accept + "\n" + bodyBase64 + "\n" + content_type + "\n" + date + "\n"+ url.path();
qDebug() << "stringToSign="<<stringToSign<<"\n";
// 2.计算 HMAC-SHA1
QString signature = HMACSha1(ak_secret.toLocal8Bit(),stringToSign.toLocal8Bit()).toBase64();
qDebug() << "signature="<<signature<<"\n";
// 3.得到 authorization header
QString authHeader = "Dataplus " + ak_id + ":" + signature;
qDebug() << "authHeader="<<authHeader<<"\n";
QNetworkRequest request;
// request.sslConfiguration().setPeerVerifyMode(QSslSocket::VerifyNone);
// request.sslConfiguration().setProtocol(QSsl::TlsV1_0);
QSslConfiguration config;
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::TlsV1_0);
request.setSslConfiguration(config);
request.setUrl(url);
request.setRawHeader(QByteArray("accept"), accept.toLocal8Bit());
request.setHeader(QNetworkRequest::ContentTypeHeader,content_type);
request.setRawHeader(QByteArray("date"), date.toLocal8Bit());
request.setRawHeader(QByteArray("Authorization"), authHeader.toLocal8Bit());
// request.setRawHeader(QByteArray("Connection"), QByteArray("keep-alive"));
if(reply != Q_NULLPTR) {//更改reply指向位置钱一定要保证之前的定义了自动delete
reply->deleteLater();
}
reply = manager->post(request,body);
// connect(reply, &QNetworkReply::finished, this, &FaceDetectThread::finishedReplay);
connect(reply, SIGNAL(finished()), this, SLOT(finishedReplay()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
// connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
// this, SLOT(slotSslErrors(QList<QSslError>)));
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),
this,SLOT(downloadProgress(qint64,qint64)));
qDebug() << "start post_al";
}
备注:bytearray是base64的图片内容
6)请求返回的数据为JSON格式 描述,字段描述细节参考官方的人脸属性识别API 调用说明,内容有点多,我就不截图。
返回数据获取示例代码如下:
void FaceDetectObject::finishedReplay()
{
QByteArray bytes = reply->readAll();
const QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
reply->deleteLater();
reply = Q_NULLPTR;
if (!redirectionTarget.isNull()) {//如果网址跳转重新请求
const QUrl redirectedUrl = url.resolved(redirectionTarget.toUrl());
qDebug()<<"redirectedUrl:"<<redirectedUrl.url()<<"\n";
url = redirectedUrl;
qDebug()<<"new_url="<<url.toString().toLocal8Bit()<<"\n";
//写出新url,测试使用
QFile f("url_.txt");//写出文件
f.open(QFile::WriteOnly);
f.write(url.toString().toLocal8Bit());
f.close();
return;
}
qDebug()<<"finished:\n";
QString html_text = bytes;
qDebug()<<"get ready,read size:"<<html_text.size();
qDebug()<< "ret_html_text:\n"<<html_text<<"\n";
//保存结果,测试使用
QFile f("result.html");//写出文件
f.open(QFile::WriteOnly);
f.write(bytes);
f.close();
}
返回结果示例:
{"face_num":1,"face_rect":[391,365,94,127],"face_prob":[1.0],"pose":[5.843783378601074,-3.454075336456299,2.8692541122436523],"landmark_num":105,"landmark":[..展示需要删除..],"iris":[417.10675048828125,415.4582214355469,4.029685020446777,460.1661071777344,418.9272155761719,4.029685020446777],"gender":[0],"age":[28],"expression":[0],"glass":[0],"dense_fea_len":1024,"dense_fea":"[..展示需要删除..]","errno":0,"request_id":"3bad9b68-b337-4c07-8661-403178948a31"}
取得返回结果后就是 业务应用的范畴了,人脸检测定位、人脸比对实现类似。基于我的业务逻辑只需要脸数、性别、年龄、笑容,示例代码如下:
//输入参数来自:QByteArray bytes = reply->readAll();
void FaceDetectObject::JsonDecode(QByteArray bytes)
{
QJsonParseError json_error;
QJsonDocument retVals = QJsonDocument::fromJson(bytes,&json_error);
if(json_error.error == QJsonParseError::NoError)
{
if(retVals.isObject())
{
int face_num_val=0 ;
QVector<bool> genders;
QVector<int> ages;
QVector<bool> expressions;
QJsonObject obj = retVals.object();
if(obj.contains("face_num"))
{
QJsonValue face_num_Json = obj.value("face_num");
qDebug() <<"face_num_Json="<< face_num_Json.toInt()<<"\n";
if(face_num_Json.isDouble())
{
face_num_val = face_num_Json.toInt();
}
}
if(obj.contains("gender"))
{
QJsonValue gender_Json = obj.value("gender");
if(gender_Json.isArray())
{
QJsonArray gender_vals = gender_Json.toArray();
qDebug() <<"gender_vals="<< gender_vals<<"\n";
foreach (QJsonValue gval, gender_vals) {
if(gval.isDouble())
genders.push_back(gval.toInt()>0?true:false);
}
if(genders.size()!=face_num_val)
{
qDebug() << QString("gender vector num(%1) is not map face_num(%2)!")
.arg(genders.size()).arg(face_num_val);
}
}else{
qDebug() << "gender_Json value type is not map Array!";
}
}
if(obj.contains("age"))
{
QJsonValue age_Json = obj.value("age");
if(age_Json.isArray())
{
QJsonArray age_vals = age_Json.toArray();
qDebug() <<"age_vals="<< age_vals<<"\n";
foreach (QJsonValue age_val, age_vals) {
if(age_val.isDouble())
ages.push_back(age_val.toInt());
}
if(ages.size()!=face_num_val)
{
qDebug() << QString("ages vector num(%1) is not map face_num(%2)!")
.arg(ages.size()).arg(face_num_val);
}
}else{
qDebug() << "age_Json value type is not map Array!";
}
}
if(obj.contains("expression"))
{
QJsonValue expression_Json = obj.value("expression");
if(expression_Json.isArray())
{
QJsonArray expression_vals = expression_Json.toArray();
qDebug() <<"expression_vals="<< expression_vals<<"\n";
foreach (QJsonValue expression_val, expression_vals) {
if(expression_val.isDouble())
expressions.push_back(expression_val.toInt()>0?true:false);
}
if(expressions.size()!=face_num_val)
{
qDebug() << QString("expressions vector num(%1) is not map face_num(%2)!")
.arg(expressions.size()).arg(face_num_val);
}
}else{
qDebug() << "expression_Json value type is not map Array!";
}
}
qDebug() << "face_num_val="<<face_num_val<<"\n"
<< "genders="<<genders<<"\n"
<< "ages="<<ages<<"\n"
<< "expressions="<<expressions<<"\n";
}
}else{
qDebug() << "QJsonDocument::fromJson fail:"<<json_error.errorString()<<"\n";
}
下面给出本实例的完整通信接口供有需要的朋友,记得换成自己的key和密钥,由于是示例代码,很多地方未做优化和产品化考虑,请大家斟酌参考:
#ifndef FACEDETECTOBJECT_H
#define FACEDETECTOBJECT_H
#include <QObject>
#include <QString>
#include <QByteArray>
#include <QImage>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QNetworkRequest>
#include <QNetworkReply>
class FaceDetectObject
: public QObject
{
Q_OBJECT
public:
FaceDetectObject(QObject * parent = 0);
~FaceDetectObject();
private:
QByteArray getMD5(QByteArray bytes_);
QByteArray HMACSha1(QByteArray key, QByteArray baseString);
QByteArray Image_To_Base64(QString image_path);
QImage Base64_To_Image(QByteArray bytearray,QString save_Path);
QByteArray Image_To_ByteArray(QString image_path);
QImage ByteArray_To_Image(QByteArray bytearray,QString save_Path);
public slots:
void do_get();
void do_post_img(QString imgFile);
void do_post(QByteArray bytearray);
void do_post_al(QByteArray bytearray);
private slots:
void finishedReplay();
void slotError(QNetworkReply::NetworkError net_error);
void downloadProgress(qint64 bytesSent, qint64 bytesTotal);
private:
QNetworkAccessManager *manager;
QUrl url;
QNetworkReply *reply;
};
#endif // FACEDETECTOBJECT_H
#include "facedetectobject.h"
#include <QBuffer>
#include <QCryptographicHash>
#include <QDateTime>
#include <QFile>
#include <QSslConfiguration>
#include <QSslSocket>
#include <QDebug>
FaceDetectObject::FaceDetectObject(QObject * parent)
: QObject(parent)
{
// //test
// QByteArray src = Image_To_ByteArray("fdtimg.jpg");
// QImage dest = ByteArray_To_Image(src,"fdtimg_c.jpg");
manager =new QNetworkAccessManager(this);
url = QUrl("http://www.baidu.com/");
reply = Q_NULLPTR;
}
FaceDetectObject::~FaceDetectObject()
{
}
void FaceDetectObject::do_get()
{
QNetworkRequest request;
request.setUrl(url);
if(reply != Q_NULLPTR) {//更改reply指向位置钱一定要保证之前的定义了自动delete
reply->deleteLater();
}
reply = manager->get(request);
// connect(reply, &QNetworkReply::finished, this, &FaceDetectThread::finishedReplay);
connect(reply, SIGNAL(finished()), this, SLOT(finishedReplay()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),
this,SLOT(downloadProgress(qint64,qint64)));
qDebug() << "start get";
}
void FaceDetectObject::do_post_img(QString imgFile)
{
qDebug()<<"do_post_img and imgfile="<<imgFile<<"\n";
QByteArray imgByteArray = Image_To_Base64(imgFile);
do_post_al(imgByteArray);
}
void FaceDetectObject::do_post(QByteArray bytearray)
{
QNetworkRequest request;
request.setUrl(url);
request.setRawHeader("Content-Type","application/x-www-form-urlencoded");
if(reply != Q_NULLPTR) {//更改reply指向位置钱一定要保证之前的定义了自动delete
reply->deleteLater();
}
reply = manager->post(request,bytearray);
// connect(reply, &QNetworkReply::finished, this, &FaceDetectThread::finishedReplay);
connect(reply, SIGNAL(finished()), this, SLOT(finishedReplay()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
// connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
// this, SLOT(slotSslErrors(QList<QSslError>)));
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),
this,SLOT(downloadProgress(qint64,qint64)));
qDebug() << "start post";
}
void FaceDetectObject::do_post_al(QByteArray bytearray)
{
qDebug()<<"img_bytearray_size="<<bytearray.size()<<"\n";
// qDebug()<<"img_bytearray="<<bytearray.data()<<"\n";
url = QUrl("https://dtplus-cn-shanghai.data.aliyuncs.com/face/attribute",QUrl::TolerantMode);
// url.toEncoded();
qDebug() <<"url="<<url.url()<<"\n";
QString ak_id = "************"; //key
QString ak_secret = "*************************"; //secret
/*
* http header 参数
*/
QString method = "POST";
QString accept = "application/json";
QString content_type = "application/json";
QLocale lo = QLocale::English;//设置QLocale为英文
QString date = lo.toString(QDateTime::currentDateTimeUtc(),"ddd, dd MMM yyyy hh:mm:ss")+QString(" GMT");
qDebug() <<"date = " << date <<"\n";
//1.对body做MD5+BASE64加密
QByteArray body = QString("{\"type\":\"1\",\"content\":\"%1\"}").arg(bytearray.data()).toLocal8Bit();
qDebug() <<"body = " << body <<"\n";
QByteArray bodyMd5 = getMD5(body);
qDebug() <<"bodyMd5 = " << bodyMd5 <<"\n";
QString bodyBase64 = bodyMd5.toBase64();
qDebug() <<"bodyBase64 = " << bodyBase64 <<"\n";
QString stringToSign = method + "\n" + accept + "\n" + bodyBase64 + "\n" + content_type + "\n" + date + "\n"+ url.path();
qDebug() << "stringToSign="<<stringToSign<<"\n";
// 2.计算 HMAC-SHA1
QString signature = HMACSha1(ak_secret.toLocal8Bit(),stringToSign.toLocal8Bit()).toBase64();
qDebug() << "signature="<<signature<<"\n";
// 3.得到 authorization header
QString authHeader = "Dataplus " + ak_id + ":" + signature;
qDebug() << "authHeader="<<authHeader<<"\n";
QNetworkRequest request;
// request.sslConfiguration().setPeerVerifyMode(QSslSocket::VerifyNone);
// request.sslConfiguration().setProtocol(QSsl::TlsV1_0);
QSslConfiguration config;
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::TlsV1_0);
request.setSslConfiguration(config);
request.setUrl(url);
request.setRawHeader(QByteArray("accept"), accept.toLocal8Bit());
request.setHeader(QNetworkRequest::ContentTypeHeader,content_type);
request.setRawHeader(QByteArray("date"), date.toLocal8Bit());
request.setRawHeader(QByteArray("Authorization"), authHeader.toLocal8Bit());
// request.setRawHeader(QByteArray("Connection"), QByteArray("keep-alive"));
if(reply != Q_NULLPTR) {//更改reply指向位置钱一定要保证之前的定义了自动delete
reply->deleteLater();
}
reply = manager->post(request,body);
// connect(reply, &QNetworkReply::finished, this, &FaceDetectThread::finishedReplay);
connect(reply, SIGNAL(finished()), this, SLOT(finishedReplay()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
// connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
// this, SLOT(slotSslErrors(QList<QSslError>)));
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),
this,SLOT(downloadProgress(qint64,qint64)));
qDebug() << "start post_al";
}
QByteArray FaceDetectObject::getMD5(QByteArray bytes_)
{
QCryptographicHash ch(QCryptographicHash::Md5);
QByteArray ret;
ch.addData(bytes_);
ret = ch.result();
return ret;
}
QByteArray FaceDetectObject::HMACSha1(QByteArray key, QByteArray baseString)
{
int blockSize = 64; // HMAC-SHA-1 block size, defined in SHA-1 standard
if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with SHA-1 compression
key = QCryptographicHash::hash(key, QCryptographicHash::Sha1);
}
QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6"
QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "/"
// ascii characters 0x36 ("6") and 0x5c ("/") are selected because they have large
// Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
for (int i = 0; i < key.length(); i++) {
innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
}
// result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64
QByteArray total = outerPadding;
QByteArray part = innerPadding;
part.append(baseString);
total.append(QCryptographicHash::hash(part, QCryptographicHash::Sha1));
QByteArray hashed = QCryptographicHash::hash(total, QCryptographicHash::Sha1);
/// 注意——>把字符串hashed转换为Hex,内存中的ASCII码arrayFromHexString
QByteArray arrayFromHexString = QByteArray::fromHex(hashed.toHex());
qDebug()<<"hmacSha1内存中的ASCII码 arrayFromHexString \n"<<arrayFromHexString.toHex();
return arrayFromHexString;
}
QByteArray FaceDetectObject::Image_To_Base64(QString image_path)
{
QImage image(image_path);
QByteArray ba;
QBuffer buf(&ba);
image.save(&buf,"jpg");
QByteArray hexed = ba.toBase64();
buf.close();
return hexed;
}
QImage FaceDetectObject::Base64_To_Image(QByteArray bytearray,QString save_Path)
{
QByteArray Ret_bytearray;
Ret_bytearray = QByteArray::fromBase64(bytearray);
QBuffer buffer(&Ret_bytearray);
buffer.open(QIODevice::WriteOnly);
QImage imageresult;
imageresult.loadFromData(Ret_bytearray);
if(save_Path != "")
{
qDebug() <<"save" ;
imageresult.save(save_Path);
}
return imageresult;
}
QByteArray FaceDetectObject::Image_To_ByteArray(QString image_path)
{
qDebug()<<"image_path="<<image_path<<"\n";
QImage image(image_path);
QByteArray ba;
QBuffer buf(&ba);
image.save(&buf,"jpg");
qDebug()<<"ba_size="<<ba.size()<<"\n";
// qDebug()<<"ba="<<ba.data()<<"\n";
buf.close();
return ba;
}
QImage FaceDetectObject::ByteArray_To_Image(QByteArray bytearray,QString save_Path)
{
QBuffer buffer(&bytearray);
buffer.open(QIODevice::WriteOnly);
QImage imageresult;
imageresult.loadFromData(bytearray);
if(save_Path != "")
{
qDebug() <<"save" ;
imageresult.save(save_Path);
}
return imageresult;
}
void FaceDetectObject::finishedReplay()
{
QByteArray bytes = reply->readAll();
const QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
reply->deleteLater();
reply = Q_NULLPTR;
if (!redirectionTarget.isNull()) {//如果网址跳转重新请求
const QUrl redirectedUrl = url.resolved(redirectionTarget.toUrl());
qDebug()<<"redirectedUrl:"<<redirectedUrl.url()<<"\n";
url = redirectedUrl;
qDebug()<<"new_url="<<url.toString().toLocal8Bit()<<"\n";
//写出新url,测试使用
QFile f("url_.txt");//写出文件
f.open(QFile::WriteOnly);
f.write(url.toString().toLocal8Bit());
f.close();
return;
}
qDebug()<<"finished:\n";
QString html_text = bytes;
qDebug()<<"get ready,read size:"<<html_text.size();
qDebug()<< "ret_html_text:\n"<<html_text<<"\n";
//保存结果,测试使用
QFile f("result.html");//写出文件
f.open(QFile::WriteOnly);
f.write(bytes);
f.close();
}
void FaceDetectObject::slotError(QNetworkReply::NetworkError net_error)
{
qDebug()<< "slotError:"<<net_error;
}
void FaceDetectObject::downloadProgress(qint64 bytesSent, qint64 bytesTotal)
{
qDebug()<< "\ndownloadProgress done:\n";
qDebug() << "bytesSent: " << bytesSent<< " " << "bytesTocal: " << bytesTotal;
}