目录
上传文件功能的实现
uploadtask的设计
设置上传的槽函数
uploadFileAction接口
uploadFile接口
定时上传文件
进度条的设计
上传文件功能实现
1.双击 ”上传文件 “的 QListWidgetItem 或者 点击 “上传” 菜单项 都会弹出一个文件对话框
2.在文件对话框选中对应的文件,保存所有文件的路径(在对话框中可以选中多个文件)
3.将对应的文件存放到 uploadtask* m_uploadtask中(uploadtask 是一个上传文件队列类型)
4.定义m_uploadTimer定时器,每0.5秒调用 uploadFileAction 进行上传文件。
5.uploadFileAction中先发送一个 md5 http请求给服务器
6.妙传失败,调用 uploadFile 接口,将文件内容上传到服务器上。
1.UploadFileInfo 是上传文件的信息的结构体
#define UPLOAD_NOT 0 //未上传
#define UPLOADING 1 //正在上传
#define UPLOAD_FINISHED 2 //上传完成
#define UPLOAD_FAILD 3 //上传失败
#define UPLOAF_FILE_EXISTE 4 //上传的文件已存在
struct UploadFileInfo
{
QString md5; //文件的md5值
QString fileName; //文件名称
QString filePath; //文件路径
qint64 size; //文件大小
FileDataProgress *fdp; //进度条
int uploadStatus; //0.未上传,1.正在上传,2.上传完成
};
2.UploadTask 是 上传任务 列表类,该 任务列表 存储的是即将上传的文件的信息 UploadFileInfo
//uploadtask.h文件
//上传任务列表类, 单例模式
class UploadTask
{
public :
//获取UploadTask唯一实例对象
static UploadTask* getInstance();
//添加文件到上传任务列表中
int appendUploadTask(QString filePath);
//判断任务列表是否为空
bool isEmpty();
//取出任务
UploadFileInfo* takeTask();
//删除任务
void delUploadTask();
//清空上传任务列表列表
void clearList();
private:
UploadTask();
~UploadTask();
//静态数据成员,类中声明,类外必须定义
static UploadTask* m_instance;
private:
QList m_fileList;
};
//uploadtask.cpp文件
//添加文件到上传任务列表中
int uploadtask::appendUploadTask(QString filePath)
{
QFileInfo* fileinfo=new QFileInfo(filePath);
if(fileinfo->size()>30*1024*1024)
{
//如果文件大于30mb,则放弃该文件
delete fileinfo;
return -1;
}
//新建一个UploadFileInfo对象
UploadFileInfo* uploadFileInfo=new UploadFileInfo;
uploadFileInfo->size=fileinfo->size();
uploadFileInfo->fileName=fileinfo->fileName();
uploadFileInfo->filePath=filePath;
uploadFileInfo->md5=Common::getInstant()->getFileMd5(filePath);
uploadFileInfo->uploadStatus=UPLOAD_NOT;//设置未上传状态
filedataProgress* upProgress=new filedataProgress;
upProgress->setName(uploadFileInfo->fileName);
uploadFileInfo->fdp=upProgress;
//将进度条插入到上传列表中
uploadlayout::getInstant()->getVLayout()->insertWidget(0,upProgress);
m_filelist.append(uploadFileInfo);
}
//判断任务列表是否为空
bool uploadtask::isEmpty()
{
return m_filelist.empty();
}
//取出任务
UploadFileInfo* uploadtask::takeTask()
{
if(m_filelist.empty()){
return nullptr;
}
UploadFileInfo* temp=m_filelist.at(0);
return temp;
}
//删除任务
void uploadtask::delUploadTask()
{
//1.取出任务,判断该任务的状态,如果是上传完成,上传失败,上传成功,该任务是会被删除掉
//2.将该任务从列表中移除
//3.删除进度条,并删除该任务的资源
if(isEmpty()){
return;
}
UploadFileInfo* temp=m_filelist.at(0);
if(temp->uploadStatus==UPLOAD_FINISHED||
temp->uploadStatus==UPLOAD_FAILD||
temp->uploadStatus==UPLOAF_FILE_EXISTE )
{
//删除该任务
qDebug()<<"删除任务";
m_filelist.removeAt(0);
//将进度条从布局中移除
uploadlayout::getInstant()->getVLayout()->removeWidget(temp->fdp);
delete temp->fdp;
delete temp;
}
}
//清空任务列表
void uploadtask::clearList()
{
int len=m_filelist.size();
for(int i=0;igetVLayout()->removeWidget(temp->fdp);//将进度条从布局中移除
delete temp->fdp;
delete temp;
}
}
点击 “上传文件" 的QListWidgetItem,将文件的上传任务 添加 到 上传队列中。
//设置“上传文件”QListWidgetItem的槽函数
//双击QListWidget窗口中的控件,则会发出一个itemDoubleClicked信号
connect(ui->listWidget,&QListWidget::itemDoubleClicked,this,[=](QListWidgetItem *item){
if(item->text()=="上传文件")
{
addUploadFile();
}
});
//设置“上传”菜单项的槽函数
connect(m_uploadAction,&QAction::triggered,this,[=]{
addUploadFile();
qDebug()<<"上传";
});
//添加上传文件
void myfile::addUploadFile()
{
//getOpenFileNames可以获取多个文件的路径
QStringList filelist=QFileDialog::getOpenFileNames();
for(int i=0;iappendUploadTask(filelist[i]);
if(res==-1){
//服务器设置最多只能上传30m大小的文件
QMessageBox::warning(this,"警告","文件大小大于30m");
}
}
}
POST http://119.23.41.13:80/md5 HTTP/1.1
Content-Type: application/json
{
"fileName": "111.rtf", //文件名
"md5": "8274425de767b30b2fff1124ab54abb5",//文件md5值,标识文件的唯一性
"token": "7b4b4922958c5cbb153df2668b714144",//验证用户身份
"user": "zhangsan"//用户信息
}
客户端根据服务器返回的 code 做出相应的动作。
"code":"005" //上传的文件已存在(别的用户正在上传该文件)
"code":"006" //秒传成功,文件已经存在
"code":"007" // 秒传失败,文件不存在,需要调用uploadFile上传文件,
"code":"011" //"Token 验证失败,身份过期
//获取m_uploadtask中的一个上传任务
void myfile::uploadFileAction()
{
if(m_uploadtask->isEmpty()){
//任务列表为空
// qDebug()<<"任务列表为空";
return;
}
UploadFileInfo* uploadFileInfo=m_uploadtask->takeTask();
//先进行快传,判断文件是否已经上传成功
//封装http请求
//如果该文件没有上传,则发送请求
if(uploadFileInfo->uploadStatus==UPLOAD_NOT)
{
uploadFileInfo->uploadStatus=UPLOADING;
QNetworkRequest request;
//从配置文件中获取到ip地址和port端口号
QString ip=Common::getInstant()->getConfValue("web_server","ip");
QString port=Common::getInstant()->getConfValue("web_server","port");
QString url = QString("http://%1:%2/md5").arg(ip).arg(port);
request.setUrl(QUrl(url));
//设置文件类型
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("application/json"));
//将data数据以QJson的格式发送给服务器
QJsonObject object;
object.insert("user", m_logininfo->user());
object.insert("token", m_logininfo->token());
object.insert("md5",uploadFileInfo->md5);
object.insert("fileName",uploadFileInfo->fileName);
QJsonDocument doc(object);
QByteArray data=doc.toJson();
//发送请求
// qDebug()<post(request,data);
connect(rely,&QNetworkReply::readyRead,this,[=]{
//响应到达,读取所有的数据
QByteArray s=rely->readAll();
qDebug() << "服务器返回数据:" << QString(s);
//将s数据转换为Json对象
QJsonParseError err;
QJsonDocument document=QJsonDocument::fromJson(s,&err);
if(err.error!=QJsonParseError::NoError){
qDebug()<<"QJson格式错误";
return;
}
//将QJson字符串转换为QJson对象
QJsonObject object1;
object1=document.object();
//获取状态码
QString value1=object1["code"].toString();
if(value1=="005"){
qDebug()<<"文件已经存在";
//文件正在上传
uploadFileInfo->uploadStatus=UPLOAF_FILE_EXISTE;
Common::getInstant()->writeRecord(m_logininfo->user(),uploadFileInfo->fileName,value1);
m_uploadtask->delUploadTask();
return;
}
if(value1=="006"){
uploadFileInfo->uploadStatus=UPLOAF_FILE_EXISTE;
Common::getInstant()->writeRecord(m_logininfo->user(),uploadFileInfo->fileName,value1);
m_uploadtask->delUploadTask();
qDebug()<<"妙传成功";
}
if(value1=="007"){
//qDebug()<<"上传文件";
//文件没有上传到服务器上,需要将文件上传到服务器上
uploadFile(uploadFileInfo);
}
if(value1=="111")
{
Common::getInstant()->writeRecord(m_logininfo->user(),uploadFileInfo->fileName,value1);
qDebug()<<"token验证失败";
}
});
}
}
uploadFile是发送一个 http请求 将文件中数据发送给服务器,服务器会将其进行保存。
POST http://119.23.41.13:80/upload HTTP/1.1
Content-Type: application/json
------WebKitFormBoundaryNr0Jm9D3w0GCiG9g //文件边界线
Content-Disposition: form-data; user="zhangsan" filename="111.rtf" md5="8274425de767b30b2fff1124ab54abb5" size=7
Content-Type: application/octet-stream
文件数据
------WebKitFormBoundaryNr0Jm9D3w0GCiG9g //文件边界线
服务器返回值:
"code":"008"
"code":"009"
//将文件上传到服务器上
/*
------WebKitFormBoundaryDQAR0QX1ojAyzAre\r\n
Content-Disposition: form-data; name="file"; filename="logo.png"\r\n
Content-Type: image/png\r\n
\r\n
真正的文件内容\r\n
------WebKitFormBoundaryDQAR0QX1ojAyzAre
*/
//将文件内容上传到服务器上
void myfile::uploadFile(UploadFileInfo *uploadFileInfo)
{
QFile file(uploadFileInfo->filePath);
file.open(QIODevice::ReadOnly | QIODevice::Text);
logininfoinstance *login = logininfoinstance::getInstant();
//getBoundary接口是随机生成一个文件边界线
QString boundary = m_common->getBoundary();
QByteArray data;
data.append(boundary);
data.append("\r\n");
data.append("Content-Disposition: form-data; ");
data.append(QString("user=\"%1\" filename=\"%2\" md5=\"%3\" size=%4")
.arg(login->user())
.arg(uploadFileInfo->fileName)
.arg(uploadFileInfo->md5)
.arg(uploadFileInfo->size));
data.append("\r\n");
data.append("Content-Type: application/octet-stream");
data.append("\r\n");
data.append("\r\n");
//上传中的数据
data.append(file.readAll());
data.append("\r\n");
data.append(boundary);//文件边界线
if (file.isOpen()) {
file.close();
}
QString url = QString("http://%1:%2/upload").arg(login->ip()).arg(login->port());
QNetworkRequest request;
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
//发送http请求
QNetworkReply *reply = m_manager->post(request, data);
if (reply == NULL) {
qDebug() << "请求失败";
return;
}
//显示文件上传进度
connect(reply, &QNetworkReply::uploadProgress, this, [=](qint64 bytesSent, qint64 bytesTotal){
//bytesSent 上传的字节数
//bytesTotal 文件需要上传的总字节数
if (bytesTotal != 0) {
//显示进度条(设置进度条)
uploadFileInfo->fdp->setValue(bytesSent, bytesTotal);
}
});
connect(reply, &QNetworkReply::finished, this, [=](){
//文件上传完成后
if (reply->error() != QNetworkReply::NoError) {
qDebug() << reply->errorString();
} else {
QByteArray s=reply->readAll();
/*
008: 上传成功
009: 上传失败
*/
//响应到达,读取所有的数据
// qDebug() << "服务器返回数据:" << QString(s);
//将s数据转换为Json对象
QJsonParseError err;
QJsonDocument document=QJsonDocument::fromJson(s,&err);
if(err.error!=QJsonParseError::NoError){
qDebug()<<"QJson格式错误";
return;
}
//将QJson字符串转换为QJson对象
QJsonObject object1;
object1=document.object();
//获取状态码
QString value1=object1["code"].toString();
if (value1== "008") {
qDebug() << "上传成功";
uploadFileInfo->uploadStatus=UPLOAD_FINISHED;
getFileCount(Normal);
} else if (value1 == "009") {
uploadFileInfo->uploadStatus=UPLOAD_FAILD;
qDebug() << "上传失败";
}
//将传输记录记录到文件中
Common::getInstant()->writeRecord(login->user(),uploadFileInfo->fileName,value1);
// writeRecord(QString user, QString fileName, QString code, QString path)
}
m_uploadtask->delUploadTask();
reply->deleteLater();
});
}
定义一个定时器,定时器每0.5秒 调用一次uploadFileAction函数,
//设置每0.5秒调用uploadFileAction
m_uploadTimer.start(500);
connect(&m_uploadTimer,&QTimer::timeout,this,[=]()
{
uploadFileAction();
});
自定义一个进度条类型filedataProgress ,包括filedataProgress.h ,filedataProgress.cpp,filedataProgress.ui 文件.
filedataProgress.ui界面设计:
//filedataProgress.cpp文件
//设置文件名
void filedataProgress::setName(QString name)
{
ui->label->setText(name);
}
//设置进度条数据
void filedataProgress::setValue(int value,int maxValue)
{
ui->progressBar->setValue(value*100/maxValue);
}
当上传任务添加到上传列表时,就将进度条显示到 上传列表中,等待客户端上传文件。
在uploadFile接口 中需要设置 QNetWorkReply的uploadProgress信号,将上传的进度显示到进度条上。。QNetWorkReply每 当上传一次文件数据,则会发出uploadProgress信号
注意: bytesTotal 不能为 0,如果为 0,则会崩溃。
//显示文件上传进度
connect(reply, &QNetworkReply::uploadProgress, this, [=](qint64 bytesSent, qint64 bytesTotal){
//bytesSent 上传的字节数
//bytesTotal 文件需要上传的总字节数
if (bytesTotal != 0) {
//显示进度条(设置进度条)
uploadFileInfo->fdp->setValue(bytesSent, bytesTotal);
}
});