当前位置: 首页 > wzjs >正文

常德网站开发百度推广查询

常德网站开发,百度推广查询,在哪里做网站比较好,重庆网站建设 cqhtwl文件下载 前端请求 箭头函数 //这个箭头函数可以形象理解为,x流入(>)x*x, //自然而然>前面的就是传入参数,>表示函数体 x > x * x//相当于 function (x) {return x * x; }//如果参数不是一个,就需要用括号()括起来…

文件下载

前端请求

箭头函数

//这个箭头函数可以形象理解为,x流入(=>)x*x,
//自然而然=>前面的就是传入参数,=>表示函数体
x => x * x//相当于
function (x) {return x * x;
}//如果参数不是一个,就需要用括号()括起来:
(x, y) => x * x + y * y

本项目的请求下载前端代码为:

 function downloadFile(resourceId, filename, progressBar, statusText) {fetch('/resource/download', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ resourceId }) //通过post方式将要下载的文件路径发送给后端}).then(response => {if (!response.ok) {throw new Error('下载失败');}const contentLength = response.headers.get('Content-Length');const total = contentLength ? parseInt(contentLength, 10) : 0;//返回内容长度const reader = response.body.getReader(); //这个可以逐块提供bodyconst chunks = [];let received = 0;const pump = () => reader.read().then(({ done, value }) => {if (done) {//如果读取完成,整个文件已下载const blob = new Blob(chunks);//将所有小段chunks转换成一个完成的blob(binary large object)const url = window.URL.createObjectURL(blob);//浏览器创建一个临时的URL地址来获取这个数据//如blob:http://localhost/17dfc4b1-df34-4a93-a6a7-6df9f1e85e0cconst a = document.createElement('a');a.href = url;a.download = filename;document.body.appendChild(a);a.click();//模拟点击浏览器的下载行为document.body.removeChild(a);window.URL.revokeObjectURL(url);//避免内存泄露progressBar.style.width = '100%';statusText.textContent = '下载完成';return;}chunks.push(value);received += value.length;//更新下载进度if (total > 0) {const percent = Math.floor((received / total) * 100);progressBar.style.width = percent + '%';progressBar.textContent = percent + '%';statusText.textContent = `下载中 ${percent}%`;} else {statusText.textContent = `下载中(未知大小)`;}//递归调用 pump(继续读取下一段)return pump();});return pump();}).catch(error => {console.error('下载出错:', error);progressBar.style.backgroundColor = 'red';statusText.textContent = '下载失败';});}//类比
// 后端:用水龙头一点点把水流出来
// 前端:接水并灌到瓶子里(Blob)
// createObjectURL:给这瓶水贴个标签(blob URL)
// 点击下载:把瓶子交给你下载
// revokeObjectURL:把标签撕掉,清理内存

对于pump函数的理解,结合箭头函数和promise

  1. reader.read()
    ○ 返回一个 Promise<{ done: boolean, value: Uint8Array }>。
    ○ done: true 表示读取完了;
    ○ value 是当前读取的一段数据(Uint8Array 格式)。
  2. 箭头函数 () => reader.read().then(…)
    ○ 这是一个返回 Promise 的函数。
    ○ done: true 表示读取完了;
    ○ value 是当前读取的一段数据(Uint8Array 格式)。
  3. 箭头函数 () => reader.read().then(({ done, value }) => { return dump()}
    ■ ()=>reader.read(),无参数传入,执行reader.read(),返回reader.read()执行的结果{done,value}。
    ■ .then({ done, value })通过上一步接收这两个数据,然后通过这两个执行相应内容;
    ■ 如果done为false,表示还没执行完成,chunks.push(value):把这一段加入缓存 ,更新进度条, 递归调用自身,继续下一段读取 (return pump())。

后端响应

 FileUtil file(filePath); 
if (!file.isValid()) //判断请求的文件是否有效
{LOG_WARN << filePath << "not exist.";resp->setStatusLine(req.getVersion(), http::HttpResponse::k404NotFound, "Not Found");resp->setContentType("text/plain");std::string resp_info="File not found";resp->setContentLength(resp_info.size());resp->setBody(resp_info);
}
//设置相应头
resp->setStatusLine(req.getVersion(), http::HttpResponse::k200Ok, "OK");
resp->setCloseConnection(false);
resp->setContentType("application/octet-stream");std::string filename = std::filesystem::path(filePath).filename().string();
LOG_INFO<<"filename:"<<filename;
resp->addHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
//设置响应格式为文件类型,并添加文件的路径
resp->setContentLength(file.size());
resp->setisFileResponse(filePath);

设计亮点

HttpResponse.h头文件中

public:bool isFileResponse() const {return isFileResponse_;}std::string getFilePath() {return filePath_;}void setisFileResponse(const std::string& path){isFileResponse_ = true;filePath_ = path;}
private:
bool                               isFileResponse_; //判断是否是文件,如果是,采用流式发送
std::string                        filePath_;

在httpserver的请求函数中判断,如果是文件类型,就调用tcpconnection先将响应头发送出去,然后将消息体分小块发送,这里设置的是8kb;如果不是文件类型,直接将整个响应发送出去
HttpServer::onRequest函数中

// 给response设置一个成员,判断是否请求的是文件,如果是文件设置为true,并且存在文件位置在这里send出去。
if (!response.isFileResponse())
{	//不是文件类型muduo::net::Buffer buf;response.appendToBuffer(&buf);conn->send(&buf);
}
else
{// 1. 构造响应头muduo::net::Buffer headerBuf;response.appendToBuffer(&headerBuf);  // 只添加状态行和头部,不包含 bodyconn->send(&headerBuf);  // 先发 header// 2. 发送文件内容(分块)const std::string filePath = response.getFilePath();std::ifstream file(filePath, std::ios::binary);// 以二进制模式打开文件if (file) {const size_t bufferSize = 8192; 			// 8KB 缓冲区char buffer[bufferSize];                  // 栈上分配缓冲区while (file) {                            // 循环直到文件读取结束或出错file.read(buffer, bufferSize);        // 读取最多 bufferSize 字节到 bufferstd::streamsize bytesRead = file.gcount(); // 实际读取的字节数if (bytesRead > 0) {conn->send(muduo::StringPiece(buffer, bytesRead));// 发送数据块}}} else {// 文件打不开,补偿错误提示muduo::net::Buffer errBuf;errBuf.append("HTTP/1.1 500 Internal Server Error\r\n\r\nFile open failed");conn->send(&errBuf);}
}

之所以是在httpserver上分块发送数据流,是为了保证代码较好的层次性,httpserver负责管理多个tcp连接,包括发送消息和接收消息等。

视频播放

      // 从请求中获取 Range 头,例如 "bytes=1000-2000"std::string rangeHeader = req.getHeader("Range");LOG_INFO << "Range Header: " << rangeHeader;// 默认起始字节 start=0,结束字节 end=文件大小-1,表示完整文件std::streamsize start = 0, end = fileSize - 1;// 标记是否是分块响应bool isPartial = false;if (!rangeHeader.empty()) {// 如果客户端带了 Range,则标记为分块传输isPartial = true;long s = 0, e = -1;// 使用 sscanf 解析格式 bytes=<start>-<end>// 注意:用户可能只写了起始,没有写结束,所以要判断 sscanf 返回值int n = sscanf(rangeHeader.c_str(), "bytes=%ld-%ld", &s, &e);start = s;if (n == 1 || e == -1) {// 如果只解析到 1 个数,或者结束为 -1,则表示读到文件末尾end = fileSize - 1;} else {// 解析到两个数,且结束不能超过文件大小end = std::min((std::streamsize)e, fileSize - 1);}// 合法性检查:start 必须小于等于 end 且小于文件大小if (start > end || start >= fileSize) {// 如果不合法,返回 416 状态码(Requested Range Not Satisfiable)resp->setStatusLine(req.getVersion(), http::HttpResponse::k416RequestedRangeNotSatisfiable, "Requested Range Not Satisfiable");char rangeValue[64];// Content-Range 必须带 "*/总大小"snprintf(rangeValue, sizeof(rangeValue), "bytes */%ld", fileSize);resp->addHeader("Content-Range", rangeValue);resp->setCloseConnection(true);resp->setContentType("text/plain");resp->setBody("Invalid Range");return;}}// 计算需要读取的 chunkSizestd::streamsize chunkSize = end - start + 1;std::vector<char> buffer(chunkSize);// 如果需要分块,最好这里限制一下 chunkSize,防止内存过大// 定位到要读的起始位置file.seekg(start, std::ios::beg);// 从文件读出 chunkSize 大小的数据到 bufferfile.read(buffer.data(), chunkSize);// === 构造响应 ===if (isPartial) {resp->setStatusLine(req.getVersion(), http::HttpResponse::k206PartialContent, "Partial Content");char rangeHeaderValue[128];snprintf(rangeHeaderValue, sizeof(rangeHeaderValue),"bytes %ld-%ld/%ld", start, end, fileSize);resp->addHeader("Content-Range", rangeHeaderValue);} else {resp->setStatusLine(req.getVersion(), http::HttpResponse::k200Ok, "OK");}resp->addHeader("Accept-Ranges", "bytes");// 无论是否分块,都要告知支持分块resp->setContentType("video/mp4");         // 设置内容类型为 mp4 视频resp->setContentLength(buffer.size());     // 设置 Content-Lengthresp->setBody(std::string(buffer.begin(), buffer.end()));  // 把读取的文件块设置到响应体}

后端涉及对请求体中的range字段进行解析,判断range字段的合法性,随后根据range字段请求内容决定是返回部分内容还是全部内容。
请求所有内容:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
依次拖动播放进度条,range字段发生改变,格式为–字段,这里是请求从某一时刻到视频结束。
请求部分内容:
在这里插入图片描述
这里请求的是从字节6000-18000大小的数据,返回的响应为
在这里插入图片描述
这里的响应头字段为206 partial content,表示响应返回的只是视频的一部分数据。


range的合法性校验
这里我手动指定range的范围为6000-18000000000000,实际是超出了请求视频的最大范围,看看最后返回的什么。使用curl(这里因为是测试,所以去掉了权限的判定,实际上运行的时候使用curl是不可行的)
在这里插入图片描述
可以看到这里返回的是文件的最大大小。

http://www.dtcms.com/wzjs/105728.html

相关文章:

  • 深圳信科做网站推广普通话心得体会
  • 网站自身维护国内最新新闻事件今天
  • wordpress菜单栏的函数调用北京seo课程培训
  • 旅游网站后台html模板seo优化诊断工具
  • 顺德公益网站制作进入百度app查看
  • 网站开发协议书由谁来写哪里有免费的网站推广软件
  • 沈阳建筑大学网络信息化中心河北seo平台
  • 安防公司网站建设整站快速排名优化
  • 阿里国际站韩语网站怎么做11月将现新冠感染高峰
  • 做报表的网站国内新闻最新5条
  • 数字媒体应用 网站开发国内免费b2b网站大全
  • 做csgo直播网站关键词seo公司真实推荐
  • 武汉网站建设seo优化阿里云网站搭建
  • 野花香社区在线观看播放seo快排软件
  • 新兴建设网站关键词优化的最佳方法
  • 计算机软件开发专业学什么seo招聘要求
  • 商城网站需要多少钱公司网站策划宣传
  • 深圳企业推广网站排名湖南专业seo推广
  • 哈尔滨建站流程做网络推广一个月的收入
  • 义乌建网站指定关键词seo报价
  • 一起做网站女装夏季裙友情链接怎么连
  • go做网站网站seo查询工具
  • 高端品牌型网站建设西安竞价推广托管
  • 奉贤做网站搜狗网站收录入口
  • ui设计与艺术设计区别seo报价单
  • 杭州网站开发wguser怎样留别人电话在广告上
  • 竞价网站策划引擎优化
  • 深圳建网站的专业公司网站设计论文
  • 西安网站优化培训网站收录服务
  • 广西疫情最新情况分布图网站seo是什么意思