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

怎样创造自己的网站中国互联网协会是做什么的

怎样创造自己的网站,中国互联网协会是做什么的,贵阳网站设计方案,大港网站开发起因 事情的起因是调试 IOS 手机下播放服务器接口返回的 mp4 文件流失败。对于没调试过移动端和 Safari 的我来说着实费了些功夫#xff0c;网上和AI也没有讲明白。好在最终大概理清楚了#xff0c;在这里整理出来供有缘人参考。 问题 因为直接用 IOS 手机的浏览器打开页面…起因 事情的起因是调试 IOS 手机下播放服务器接口返回的 mp4 文件流失败。对于没调试过移动端和 Safari 的我来说着实费了些功夫网上和AI也没有讲明白。好在最终大概理清楚了在这里整理出来供有缘人参考。 问题 因为直接用 IOS 手机的浏览器打开页面去播放也能复现所以问题出现在 IOS上的 Safari 浏览器上。 问题主要分两类 一、视频能播放但是不主动设置 poster就不显示默认的 poster二、视频不能播放 首先文件是没有问题的是主流浏览器都支持的视频编码格式为 H.264(AVC) 的 .mp4 文件由文件格式导致的播放失败问题网上很多这里就不赘述了。 问题复现和解决 因为其他浏览器可以正常播放所以用来对比差异时我直接在PC端Edge查看信息而 Safari 是在手机上访问项目页面以及局域网下访问本地运行的 web 服务页面。 用到的工具如下 Live Server 用来运行本地 web 页面node nodemon Express 用来运行文件服务模拟后端接口nodemon 方便实时同步运行修改内容video.js 播放视频的插件直接用 CDN。vConsole 移动端查看控制台直接用 CDN 编写简单 demo 排查问题的时候我是一点一点将实现方式还原到最原始的方式 后端接口 video.js后端接口 video标签本地运行的接口 video标签本地文件 video标签 所以这里再梳理就可以反向从最简单的方式排查在本地写个用本地视频文件 video标签的demo为了后续排查方便将事件日志也打印出来。 视频资源我用的 https://vjs.zencdn.net/v/oceans.mp4 下载到本地不过它的开头是黑屏淡入的为了方便区分 “不显示poster”我将开头的几秒黑屏裁剪掉了。 也可以用安卓手机录制一个默认就是支持播放的 mp4 文件。PS别用QQ录屏编码不对 !DOCTYPE html html langenheadmeta charsetUTF-8 /meta nameviewport contentwidthdevice-width, initial-scale1.0 /script srchttps://unpkg.com/vconsolelatest/dist/vconsole.min.js/scriptscriptvar vConsole new window.VConsole()/scriptstyle#video {width: 480px;height: 25vh;max-width: 100%;/* 背景色用于在视频不显示poster时查看占位 */background: pink;border: 4px solid pink;}/style/headbodydiv classvideo-boxvideo idvideo srcoceans.mp4 controls preloadauto playsinlinetrue muted/video/divdiv idlogh2日志方便手机上的 Safari 查看/h2div classlog__content/div/divscript// 打印日志function log(msg) {// 页面上打印日志const logContentEl document.querySelector(.log__content)const pEl document.createElement(p)pEl.textContent msglogContentEl.appendChild(pEl)// 控制台打印日志console.log(msg)}const video document.getElementById(video)// 主要关心的事件const eventNames [{ name: abort, desc: 当音频/视频的加载已放弃时触发 },{ name: canplay, desc: 当浏览器可以开始播放音频/视频时触发 },{ name: canplaythrough, desc: 当浏览器预计能够在不因缓冲而停顿的情况下持续播放指定的音频/视频时触发 },{ name: durationchange, desc: 当音频/视频的时长已更改时触发 },{ name: error, desc: 当在音频/视频加载期间发生错误时触发 },{ name: loadeddata, desc: 当浏览器已加载音频/视频的当前帧时触发 },{ name: loadedmetadata, desc: 当浏览器已加载音频/视频的元数据时触发 },{ name: loadstart, desc: 当浏览器开始查找音频/视频时触发 },{ name: play, desc: 当音频/视频已开始或不再暂停时触发 },{ name: playing, desc: 当音频/视频在因缓冲而暂停或停止后已就绪时触发 },{ name: progress, desc: 当浏览器正在下载音频/视频时触发 },{ name: timeupdate, desc: 当音频/视频的播放位置发生改变时触发 },{ name: waiting, desc: 当视频由于需要缓冲下一帧而停止等待时触发 }]// 注册video事件监听器eventNames.forEach(v {video.addEventListener(v.name, () {// 打印日志log(【readyState: ${video.readyState}】 ${v.name}: ${v.desc})})})/script/body /html preload 默认是 metadata和 auto 的日志结果一样。为了排除一些可能性我将其设置了 auto。 playsinlinetrue 防止 IOS 播放视频时自动打开全屏。 问题1 不显示预览图 demo 页面加载后发现Safari 浏览器没有显示视频的预览图 Edge 显示了预览图 通过日志发现Safari 加载完元数据后(loadedmetadata)就不会继续加载了。 我们知道视频的预览图就是 video 标签的 poster 属性当 poster 有值时就会显示指定的图片当 poster 没有值时浏览器就会自动处理而不同浏览器的处理方式也不一样可能会有这几种情况 如果配置了预加载显示加载后的第一帧或第二、三桢。什么都不显示或显示默认的播放器背景样式 从日志可以看到Safari 只加载了元数据并没有加载画面。查看官方文档得到了解答 再看 poster 的说明 既然如此那只能提供一个 poster 来解决了。 问题2 视频不能播放 问题复现 播放 demo 示例里的视频是可以正常播放的。 将播放方式改成 video.js 本地文件播放是正常的。代码如下 !DOCTYPE html html langenheadmeta charsetUTF-8 /meta nameviewport contentwidthdevice-width, initial-scale1.0 /link hrefhttps://unpkg.com/video.js7.10.2/dist/video-js.min.css relstylesheet /script srchttps://unpkg.com/vconsolelatest/dist/vconsole.min.js/scriptscriptvar vConsole new window.VConsole()/scriptstyle#video {width: 480px;max-width: 100%;height: 25vh;/* 背景色用于在视频不显示poster时查看占位 */background: pink;border: 4px solid pink;}/style/headbodyvideo idvideo classvideo-js controls preloadauto playsinlinetrue mutedsource srcoceans.mp4 typevideo/mp4 //videodiv idlogh3日志方便手机上的 Safari 查看/h3div classlog__content/div/divscript srchttps://unpkg.com/video.js7.10.2/dist/video.min.js/scriptscript// 打印日志function log(msg) {// 页面上打印日志const logContentEl document.querySelector(.log__content)const pEl document.createElement(p)pEl.textContent msglogContentEl.appendChild(pEl)// 控制台打印日志console.log(msg)}// 主要关心的 videojs 事件const eventNames [{ name: abort, desc: 当音频/视频的加载已放弃时触发 },{ name: canplay, desc: 当浏览器可以开始播放音频/视频时触发 },{ name: canplaythrough, desc: 当浏览器预计能够在不因缓冲而停顿的情况下持续播放指定的音频/视频时触发 },{ name: durationchange, desc: 当音频/视频的时长已更改时触发 },{ name: error, desc: 当在音频/视频加载期间发生错误时触发 },{ name: loadeddata, desc: 当浏览器已加载音频/视频的当前帧时触发 },{ name: loadedmetadata, desc: 当浏览器已加载音频/视频的元数据时触发 },{ name: loadstart, desc: 当浏览器开始查找音频/视频时触发 },{ name: play, desc: 当音频/视频已开始或不再暂停时触发 },{ name: playing, desc: 当音频/视频在因缓冲而暂停或停止后已就绪时触发 },{ name: progress, desc: 当浏览器正在下载音频/视频时触发 },{ name: timeupdate, desc: 当音频/视频的播放位置发生改变时触发 },{ name: waiting, desc: 当视频由于需要缓冲下一帧而停止等待时触发 }]const player videojs(video)player.ready(() {// 注册video事件监听器eventNames.forEach(v {player.on(v.name, () {// 打印日志log(【readyState: ${player.readyState()}】 ${v.name}: ${v.desc})})})})/script/body /html 然后换成后端接口地址Edge 可以播放但是 Safari 就失败了。 video 标签方式 video.js 方式报错 The media could not be loaded, either because the server or network failed or because the format is not supported. 我肯定不是服务器网络故障也不是文件格式不支持所以原因只能是接口了。 本地模拟接口 为了不麻烦后端同事我只能在本地搭建一个服务器模拟项目接口。 搭建服务 创建 app.js 文件 const express require(express) const fs require(fs) const path require(path)const app express()app.get(/, (req, res) {res.send(Stupid IOS) })app.listen(3000, () {const ip 192.169.3.7 // 我的局域网ipconsole.log(server is running on http://${ip}:3000) }) # 安装依赖 npm i express # 已安装 nodemon所以直接使用 nodemon app.js访问 http://192.169.3.7:3000/。把 oceans.mp4 视频文件放到 app.js 同目录下后面就编写接口就行。 方式1 使用 express 封装好的方法返回文件流 // 方式1: 使用封装好的方法返回文件流 app.get(/type1/:file, (req, res) {const fileName req.params.fileconst filePath path.join(__dirname, fileName)res.sendFile(filePath) })文件地址http://192.169.3.7:3000/type1/oceans.mp4 video标签和 video.js 播放都正常。 方式2 手动读取全部文件流并返回 同样是接口本地模拟的正常项目接口不能播放那就继续更细致的模拟手动读取文件流设置相同的响应头。 查看项目接口的响应头排除一些范围只模拟有可能影响的 // 方式2: 手动读取全部文件流并返回 app.get(/type2/:file, (req, res) {const fileName req.params.fileconst filePath path.join(__dirname, fileName)// 打印请求头console.log(req.headers)fs.stat(filePath, (err, stats) {if (err) {return res.status(404).send(file not found)}// 设置响应头res.set({Accept-Ranges: bytes, // 支持 Range 请求Cache-Control: no-cache, no-store, // 不缓存Content-Type: video/mp4;charsetUTF-8,Content-Range: bytes 0-${stats.size - 1}/${stats.size},Content-Length: stats.size,Content-Disposition: attachment;filenameoceans.mp4,ETag: ${stats.ino.toString()}-${stats.size.toString()}-${Date.now().toString()},Last-Modified: stats.mtime.toUTCString(),Pragma: no-cache})const stream fs.createReadStream(filePath)stream.pipe(res)}) })文件地址http://192.169.3.7:3000/type2/oceans.mp4 问题依旧存在于是查看官方文档找到一段说明 Range 请求就是范围请求或分块传输客户端通过请求头 Range 指定当前请求想要获取的数据子节范围服务器根据这个范围读取文件流并将读取的内容返回给客户端。 通常服务器会返回 206 状态码表示范围请求的响应结果。并且需要在响应头中包含 Content-Range 字段指明实际返回的数据范围以及整个资源的总大小。 可是我加了支持 range 请求的响应头啊Accept-Ranges: bytes。 继续看文档下面介绍了如何确认服务器是否支持 range 请求 大概意思就是主动发送一个指定范围 100 bytes 的请求看返回的数据是100 bytes那就是支持如果返回了整个文件那就是不支持。 查看之前服务器中打印的请求头Range 请求头的值 Edge 是 0-表示获取整个资源Safari 是 0-1表示获取位置0 和 1 的子节的资源注意可不是从开头到第1个子节这个范围的请求数是2个子节。 因为我每次都返回的完整的文件流没有按照 Safari 的要求范围处理所以属于不支持。 看来仅仅配置响应头是不行的还要正确处理请求头中的指定范围。 方式3 手动读取指定范围的文件流并返回-分块传输 原来 Safari 会先发送一个获取范围为 bytes0-1 的请求以测试服务器是否支持 range 请求。 于是我手动改了下响应头去掉那些没有影响的还是返回整个文件流 // 设置响应头res.set({Accept-Ranges: bytes, // 支持 Range 请求Cache-Control: no-cache, no-store, // 不缓存Content-Type: video/mp4;charsetUTF-8,Content-Range: bytes 0-1/${stats.size},Content-Length: stats.size,})看来仅仅伪造响应头还是不行还要返回正确大小的文件流那就编写分块传输的接口 // 方式3: 手动读取指定范围的文件流并返回-分块传输 app.get(/type3/:file, (req, res) {const fileName req.params.fileconst filePath path.join(__dirname, fileName)// 查看请求头的范围console.log(req.headers.range)fs.stat(filePath, (err, stats) {if (err) {return res.status(404).send(file not found)}// 设置响应头res.set({Accept-Ranges: bytes, // 支持 Range 请求Cache-Control: no-cache, no-store, // 不缓存Content-Type: video/mp4})const range req.headers.rangelet partslet start 0let end stats.size - 1if (range) {parts range.replace(/bytes/, ).split(-)start parseInt(parts[0], 10)end parts[1] ? parseInt(parts[1], 10) : stats.size - 1}if (start stats.size || end stats.size) {// 如果Range请求超出文件大小返回416状态码return res.status(416).end()}if (start 0 end 0) {// 处理 Range 请求res.set({Content-Range: bytes ${start}-${end}/${stats.size},Content-Length: end - start 1})// 部分内容状态码返回200浏览器也能正常处理res.status(206)}const stream fs.createReadStream(filePath, { start, end })stream.pipe(res)}) })终于视频可以正常播放了video.js 也可以播放。 Safari 的校验还挺严格如果服务器正确处理了这个2子节的请求Safari 就会开始正式发送正常范围的请求。 服务器的日志可以体现出来 bytes0-1 bytes0-51883958 bytes196608-51883958 bytes458752-51883958最终查看了后端代码果然接口没有处理 range 请求在修改逻辑后功能终于正常。 继续伪造 range 接口 为了搞清楚 Safari 的校验到底有多严格我再次尝试模拟了一下第一次请求的响应 const range req.headers.rangelet partslet start 0let end stats.size - 1// 增加逻辑 start-----------------------------------------if (range bytes0-1) {// 设置响应头res.set({Accept-Ranges: bytes, // 支持 Range 请求Cache-Control: no-cache, no-store, // 不缓存Content-Type: video/mp4;charsetUTF-8,Content-Range: bytes 3-3/${stats.size}, // 随便写个范围Content-Length: 2})const stream fs.createReadStream(filePath, { start:100, end:200 }) // 随便获取个范围但不能少于2stream.pipe(res)return res.status(206) // 随便返回个状态码}// 增加逻辑 end-----------------------------------------if (range) {parts range.replace(/bytes/, ).split(-)start parseInt(parts[0], 10)end parts[1] ? parseInt(parts[1], 10) : stats.size - 1}再次提醒bytes0-1 表示请求的位置是0 和 1的子节是2个子节而不是 1-01 的子节数量。 不断测试下发现只要满足这几个要求Safari 就认为接口支持 range 请求 Content-Length 要正确。Content-Range 的范围要合理。返回了不少于 Range 请求头要求大小的文件流数据。 而下面这几点不影响 Safari 的校验结果 Content-Range 的范围和 Range 的指定范围不一样读取的数据范围和 Range 的指定范围不一样返回任意状态码 总结 IOS 上的 Safari 不支持 video 预加载(preload)浏览器不会自动提取帧画面作为默认的 poster 预览图Safari 上使用 video 播放视频必须支持并正确处理 Range 范围请求浏览器会先发送 bytes0-1 范围的请求来测试服务器是否支持 Range 请求如果校验成功就会继续发送正常范围的 Range 请求。否则不再请求资源。
http://www.w-s-a.com/news/992564/

相关文章:

  • 医院网站建设的目标wordpress中英文网站模板
  • 门户型网站开发难度网站导航栏有哪些
  • 推荐做任务网站软件定制开发哪家好
  • 邯郸兄弟建站第三方仓储配送公司
  • 商丘家具网站建设wordpress 添加代码
  • 基础建设的网站有哪些内容成都科技网站建设咨询电话
  • 券多多是谁做的网站招聘网站开发模板
  • 网站主机一般选哪种的企业数字展厅
  • 网站建设该如何学衡水建设局网站首页
  • 高校网站建设工作总结番禺网站开发哪家好
  • 苏州 网站的公司wordpress主页代码
  • 怎么用html做图片展示网站外贸网站建设推广费用
  • 可以做本地生活服务的有哪些网站中油七建公司官网
  • 如何建设谷歌网站网站建设优点
  • 做网站的目标是什么产品宣传片制作公司
  • 柳州建设公司网站辽宁建设工程信息网评标专家入库
  • 合肥建设学校官方网站excel导入wordpress
  • 禹城网站设计做网站需要考虑哪些
  • 深圳做营销网站建设wordpress添加文章封面
  • 阿里云的网站建设方案织梦和wordpress哪个安全
  • 聊城网站建设公司电话wordpress怎么重新配置文件
  • 创业如何进行网站建设泰州公司注册
  • 免费网站建设培训学校手机百度高级搜索入口在哪里
  • 建站经验安徽六安发现一例新冠阳性检测者
  • 滨州内做网站系统的公司汕头网络营销公司
  • 苏州制作网站的公司哪家好wordpress google搜索
  • c语言做项目网站wordpress博客被书为什么还
  • 企业建站用什么系统网站建设补充协议模板
  • 常州网站关键字优化淘客网站怎么做排名
  • 全flash网站制作教程网站做进一步优化