文章目录
用nodjs来搭建一个本地的服务器,用qt编写程序实现文件的上传和断点续传的功能
nodejs服务器搭建
先去安装一个nodejs,然后在文件夹中npm init -y
初始化nodejs项目,npm install express multer
安装所需要的包,在项目目录创建一个server.js文件,并添加下面的代码,最后用node server.js
启动服务器,速成参考Node.js 教程 | 菜鸟教程 (runoob.com)
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
const port = 3000;
// 提供静态文件服务
app.use(express.static('uploads'));
// 处理文件上传的 PUT 请求
app.put('/upload', (req, res) => {
// 获取文件名和扩展名
const originalFileName = req.headers['x-filename'];
const filePath = path.join(__dirname, 'uploads', originalFileName);
const range = req.headers['range'];
let start = 0;
// 处理断点续传
if (range) {
const parts = range.replace(/bytes=/, "").split("-");
start = parseInt(parts[0], 10);
}
// 创建文件写入流,从指定位置开始写入
const uploadStream = fs.createWriteStream(filePath, { flags: 'a', start });
// 处理文件上传数据
req.on('data', chunk => {
uploadStream.write(chunk);
});
// 处理上传完成
req.on('end', () => {
uploadStream.end();
res.end(`File uploaded successfully as ${originalFileName}`);
console.log(`File uploaded successfully as ${originalFileName}`);
});
// 处理上传中断
req.on('aborted', () => {
uploadStream.end();
console.log('Upload aborted.');
});
// 处理上传错误
req.on('error', err => {
uploadStream.end();
console.error('Upload error:', err);
});
});
// 启动服务器
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
qt代码
- fileuploader.h
#ifndef FILEUPLOADER_H
#define FILEUPLOADER_H
#include <QDebug>
#include <QFile>
#include <QFileDialog>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QObject>
#include <QProgressBar>
#include <QPushButton>
#include <QUrl>
#include <QVBoxLayout>
class FileUploader : public QWidget
{
Q_OBJECT
public:
explicit FileUploader(QWidget *parent = nullptr);
~FileUploader();
signals:
private slots:
void selectFile();
void startUpload();
void pauseUpload();
void handleUploadProgress(qint64 bytesSent, qint64 BytesTotal);
void handleUploadFinished();
public:
QVBoxLayout *layout; //定义垂直布局
QHBoxLayout *hLayout1; //定义水平布局
QHBoxLayout *hLayout2;
QPushButton *selectButton;
QPushButton *startButton;
QPushButton *pauseButton;
QLineEdit *filePathEdit;
QProgressBar *progressBar;
QWidget *centralWidget;
private:
QNetworkAccessManager *manager;
QNetworkReply *reply;
QFile *file;
QString selectedFilePath;
qint64 uploadBytes; //记录已上传字节数
bool isPaused; //是否已暂停
};
#endif // FILEUPLOADER_H
- fileuploader.cpp
#include "fileuploader.h"
FileUploader::FileUploader(QWidget *parent)
: QWidget{parent}
{
//窗口初始化
layout = new QVBoxLayout();
hLayout1 = new QHBoxLayout();
hLayout2 = new QHBoxLayout();
selectButton = new QPushButton("选择文件", this);
startButton = new QPushButton("开始传输", this);
pauseButton = new QPushButton("暂停传输", this);
filePathEdit = new QLineEdit(this);
progressBar = new QProgressBar(this);
// progressBar->setRange(0, 100);
// progressBar->setValue(0);
hLayout1->addWidget(selectButton);
hLayout1->addWidget(filePathEdit);
hLayout1->addWidget(startButton);
layout->addLayout(hLayout1);
hLayout2->addWidget(progressBar);
hLayout2->addWidget(pauseButton);
layout->addLayout(hLayout2);
centralWidget = new QWidget(this);
centralWidget->setLayout(layout);
//网络请求部分初始化
manager = new QNetworkAccessManager(this);
reply = nullptr;
file = nullptr;
uploadBytes = 0;
isPaused = false;
//信号槽注册
connect(selectButton, &QPushButton::clicked, this, &FileUploader::selectFile);
connect(startButton, &QPushButton::clicked, this, &FileUploader::startUpload);
connect(pauseButton, &QPushButton::clicked, this, &FileUploader::pauseUpload);
}
FileUploader::~FileUploader()
{
reply->deleteLater();
file->close();
file->deleteLater();
file = nullptr;
}
void FileUploader::selectFile()
{
selectedFilePath = QFileDialog::getOpenFileName(this, "选择一个文件");
if (!selectedFilePath.isEmpty()) {
filePathEdit->setText(selectedFilePath);
}
}
//槽函数,处理开始上传按钮
void FileUploader::startUpload()
{
if (selectedFilePath.isNull()) {
qDebug() << "没有选择文件";
}
QUrl url("http://localhost:3000/upload");
QNetworkRequest request(url);
//设置头部字段,application/octet-stream是一个通用的二进制数据类型
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/octet-stream");
//提取文件名并转为utf8编码的字节数组,作为http头部字段x-filename的值
request.setRawHeader("x-filename", QFileInfo(selectedFilePath).fileName().toUtf8());
file = new QFile(selectedFilePath);
if (!file->open(QIODevice::ReadOnly)) {
qDebug() << "文件打开错误";
return;
}
qDebug() << "...........................";
//如果文件尚未打开或已经关闭,重新打开
if (file == nullptr || !file->isOpen()) {
file = new QFile(selectedFilePath);
if (!file->open(QIODevice::ReadOnly)) {
qDebug() << "文件打开错误";
return;
}
}
if (uploadBytes > 0) {
request.setRawHeader("Range", QByteArray("bytes=") + QByteArray::number(uploadBytes) + "-");
file->seek(uploadBytes);
}
//发送put请求
reply = manager->put(request, file);
connect(reply, &QNetworkReply::uploadProgress, this, &FileUploader::handleUploadProgress);
connect(reply, &QNetworkReply::finished, this, &FileUploader::handleUploadFinished);
}
//槽函数,处理暂停按钮
void FileUploader::pauseUpload()
{
if (reply && reply->isRunning()) {
reply->abort();
isPaused = true;
pauseButton->setText("继续");
qDebug() << uploadBytes;
} else if (isPaused) {
isPaused = false;
startUpload(); //继续上传
pauseButton->setText("暂停");
}
}
//槽函数,处理上传进度
void FileUploader::handleUploadProgress(qint64 bytesSent, qint64 BytesTotal)
{
uploadBytes += bytesSent;
progressBar->setMaximum(BytesTotal);
progressBar->setValue(uploadBytes);
}
//槽函数,传输完成
void FileUploader::handleUploadFinished()
{
if (reply->error() == QNetworkReply::NoError) {
qDebug() << "上传成功";
QMessageBox::information(this, "提示", "上传成功");
uploadBytes = 0;
} else {
qDebug() << "上传失败" << reply->errorString();
}
}
- mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
uploader = new FileUploader(this);
//初始化窗口界面
setCentralWidget(uploader->centralWidget);
}
MainWindow::~MainWindow()
{
delete ui;
}