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

网站备案规则网站排名推广软件

网站备案规则,网站排名推广软件,视差 网站,广东省企业诚信建设促进会网站开发环境#xff1a; 基于若依开源框架的前后端分离版本的实践#xff0c;后端java的springboot#xff0c;前端若依的vue2#xff0c;做一个分片上传视频并分段播放的功能#xff0c;因为是小项目#xff0c;并没有专门准备文件服务器和CDN服务#xff0c;后端也是套用…开发环境 基于若依开源框架的前后端分离版本的实践后端java的springboot前端若依的vue2做一个分片上传视频并分段播放的功能因为是小项目并没有专门准备文件服务器和CDN服务后端也是套用的若依的上传功能 实现思路 前端根据视频文件计算出文件md5值前端按照指定大小截取视频执行分片上传可优化先使用文件MD5检查文件是否已上传后端实现接收分片的接口当已上传分片数等于总分片数时执行合并分片得到原视频文件后端使用ffmpeg按照时间进行视频分割切割时间根据视频清晰度不同而不同得到m3u8文件和ts文件列表后端保存视频信息和文件实际保存地址并提供查询接口前端使用流播放器播放视频文件 代码实现 1. vue的分片上传 前端分片上传功能按照以下步骤实现 1.1先要写一个上传组件这里使用elementUI的上传组件 在 :auto-upload 设置的视频直接不解释上传即选择好本地文件就上传 在 :before-upload 中需要计算好文件的md5值然后去后端查看文件是否已被上传 在 :http-request 中实现具体的分片上传逻辑 在 :action 虽然设置了上传地址但是任然是以http-request设置的方法为准只是不设置会报错 el-form-item label视频文件 propfile v-ifform.idnullel-upload refupload:actionuploadUrl:on-erroronError:before-uploadbeforeUpload:before-removebeforeRemove:auto-uploadtrue:limit1:http-requestchunkedUpload:on-progressonProgressdiv styleborder: 1px dashed #c0ccda;padding: 1rem;i classel-icon-upload/idiv classel-upload__text将文件拖到此处或em点击上传/em/div/divdiv classel-upload__tip slottip只能上传mp4文件且不超过500M/divel-progress :percentageuploadPercentage statussuccess/el-progress/el-upload /el-form-item1.2上传方法的js 我使用了两个后端接口 一个是 testUploadVideo 判断文件是否存在是若依分装的请求 一个是 process.env.VUE_APP_BASE_API ‘/manage/video/upload’单独用axios执行上传分片 script import { addVideo, getVideo, testUploadVideo, updateVideo } from /api/manage/video import SparkMD5 from spark-md5 import axios from axiosexport default {name: videoWin,data() {return {uploadUrl: process.env.VUE_APP_BASE_API /manage/video/upload, //文件上传的路径uploadPromises: [], // 记录并发上传分片的线程uploadPercentage:0 //上传进度}},,methods: {beforeUpload: async function(file) {// 在上传之前获取视频的宽高和分辨率const video document.createElement(video)video.src URL.createObjectURL(file)video.preload metadataconst loadedMetadata new Promise(resolve {video.onloadedmetadata () {window.URL.revokeObjectURL(video.src)const width video.videoWidthconst height video.videoHeightconsole.log(视频宽高:, width, height)this.form.width widththis.form.height heightresolve();}});// 等待视频的宽高和分辨率获取完成await loadedMetadata;// 计算文件的md5值const reader new FileReader()const md5Promise new Promise(resolve {reader.onload () {const spark new SparkMD5.ArrayBuffer()spark.append(reader.result)const md5 spark.end(false)this.form.identifier md5 // 将MD5值存储到form中resolve(md5);}});reader.readAsArrayBuffer(file); // 读取文件内容并计算MD5值const md5 await md5Promise;// 检查文件是否已被上传const response await testUploadVideo(md5);console.log(判断文件是否存在, response)if (response.msg 文件已存在秒传成功) {console.log(文件已存在)// 取消上传this.$refs.upload.abort(file);return false;} else {return true;}},chunkedUpload({ file }) {const totalSize file.sizeconst chunkCount Math.ceil(totalSize / (5 * 1024 * 1024)) // 每个分片5MB// 创建分片上传请求数组// 上传分片for (let i 0; i chunkCount; i) {const start i * (5 * 1024 * 1024)const end Math.min((i 1) * (5 * 1024 * 1024), totalSize)const chunk file.slice(start, end)const formData new FormData()formData.append(file, chunk)formData.append(filename, file.name)formData.append(totalChunks, chunkCount)formData.append(chunkNumber, i)formData.append(identifier, this.form.identifier) // 添加文件的MD5值作为参数// 发送分片上传请求const source axios.CancelToken.source() // 创建cancelTokenconst uploadPromise this.uploadChunk(formData, source.token, (progressEvent) {console.log(更新进度, progressEvent)this.uploadPercentage Math.round((progressEvent.loaded / progressEvent.total) * 100) // 更新进度条的值;}).catch(error {console.error(分片上传失败, error)// 弹出告警消息this.$message({type: error,message: 视频上传失败})})this.uploadPromises.push({ promise: uploadPromise, source }) // 保存cancelToken}// 等待所有分片上传完成return Promise.all(this.uploadPromises).then(responses {console.log(分片上传完成, responses)}).catch(error {console.error(分片上传失败, error)})},/**更新进度*/onProgress(event, file) {this.uploadPercentage Math.floor((event.loaded / event.total) * 100);},/**上传分片*/uploadChunk(formData, onProgress) {return axios.post(process.env.VUE_APP_BASE_API /manage/video/upload, formData, {onUploadProgress: onProgress // 添加进度回调}).then(response {console.log(分片上传成功, response.data)})},/**上传分片失败*/onError(error, file, fileList) {console.error(上传失败, error)},// 取消上传请求beforeRemove(file, fileList) {this.form.identifier nullreturn true}} } /script2. 后端接口实现 2.1 控制层代码 RestController RequestMapping(/manage/video) CrossOrigin // 允许跨域 public class ManageVideoController extends BaseController {Autowiredprivate IManageVideoService manageVideoService;/*** 上传分片前校验文件是否存在** return*/GetMapping(/preUpload)public AjaxResult preUpload(RequestParam(fileMd5) String fileMd5) {return manageVideoService.checkExists(fileMd5);}/*** 上传分片** return*/PostMapping(/upload)public AjaxResult fragmentation(ModelAttribute UploadPO uploadPO) {return manageVideoService.uploadChunk(uploadPO);} }2.1 服务层代码 接收到分片上传文件后经历以下步骤 再次校验是否文件已存在不存在就保存临时分片文件校验已上传分片数是否等于总分篇数如果是则合并将临时文件合并和源mp4文件获取视频的时长和大小因为ffmpeg不支持按照大小拆分如果只是按照固定时长拆分20s可能是2M也可能是34M无法达到拆分视频以缩短预览视频等待时间的目的执行视频拆分生成playlist.m3u8和一系列ts文件重写m3u8文件的ts地址1是因为若依开发环境和线上环境的指定前缀不一致2是因为本地开发没开nginx转发静态资源线上也没开文件服务 Overridepublic AjaxResult checkExists(String fileMd5) {String fileUploadDir RuoYiConfig.getProfile() /video;//判断文件是否已被上传String videoFile fileUploadDir / fileMd5 .mp4;File file new File(videoFile);if (file.exists()) {return AjaxResult.success(文件已存在秒传成功);}return AjaxResult.success();}Overridepublic AjaxResult uploadChunk(UploadPO uploadPO) {String fileUploadTempDir RuoYiConfig.getProfile() /videotmp;String fileUploadDir RuoYiConfig.getProfile() /video;// 获得文件分片数据MultipartFile fileData uploadPO.getFile();// 分片第几片int index uploadPO.getChunkNumber();//总分片数int totalChunk uploadPO.getTotalChunks();// 文件md5标识String fileMd5 uploadPO.getIdentifier();//判断文件是否已被上传String videoFile fileUploadDir / fileMd5 .mp4;File file new File(videoFile);if (file.exists()) {return AjaxResult.success(文件已存在秒传成功);}String newName fileMd5 index .tem;File uploadFile new File(fileUploadTempDir / fileMd5, newName);if (!uploadFile.getParentFile().exists()) {uploadFile.getParentFile().mkdirs();}try {fileData.transferTo(uploadFile);// 判断总分片数是否等于当前目录下的分片文件数量int currentChunkCount getChunkCount(fileUploadTempDir / fileMd5);if (totalChunk currentChunkCount) {// 调用合并方法merge(fileMd5, fileUploadTempDir, fileUploadDir);//根据运行环境分别调用ffmpegString os System.getProperty(os.name).toLowerCase();String m3u8Dir fileUploadDir / fileMd5;File m3u8FileDir new File(m3u8Dir);if (!m3u8FileDir.exists()) {m3u8FileDir.mkdirs();}//计算视频总时长和视频大小确定视频的分段时长String mp4File fileUploadDir / fileMd5 .mp4;//每个2M分片的毫秒数long duration getTsDuration(mp4File);// 异步执行视频拆分if (os.contains(win)) {mp4ToM3u8ForWindow(fileMd5, mp4File, m3u8Dir, duration);} else {mp4ToM3u8ForLinux(fileMd5, mp4File, m3u8Dir, duration);}}//执行成功返回 urlreturn AjaxResult.success();} catch (IOException | InterruptedException e) {log.error(上传视频失败{}, e.toString());FileUtil.del(fileUploadTempDir / fileMd5); //删除临时文件FileUtil.del(videoFile); //删除视频源文件FileUtil.del(fileUploadDir / fileMd5); //删除分段ts视频return AjaxResult.error(502, 上传视频失败);} catch (EncoderException e) {log.error(视频切割时计算分段时长失败{}, e.toString());FileUtil.del(fileUploadTempDir / fileMd5); //删除临时文件FileUtil.del(videoFile); //删除视频源文件FileUtil.del(fileUploadDir / fileMd5); //删除分段ts视频return AjaxResult.error(502, 上传视频失败);}}/*** 获取当前目录下的分片文件数量** param directoryPath* return*/private int getChunkCount(String directoryPath) {File directory new File(directoryPath);if (!directory.exists() || !directory.isDirectory()) {return 0;}File[] files directory.listFiles((dir, name) - name.endsWith(.tem));return files ! null ? files.length : 0;}/*** 合并分片** param uuid* return*/public void merge(String uuid, String fileUploadTempDir, String fileUploadDir) throws IOException {File dirFile new File(fileUploadTempDir / uuid);//分片上传的文件已经位于同一个文件夹下方便寻找和遍历当文件数大于十的时候记得排序用冒泡排序确保顺序是正确的String[] fileNames dirFile.list();Arrays.sort(fileNames, (o1, o2) - {int i1 Integer.parseInt(o1.substring(o1.indexOf(uuid) uuid.length()).split(\\.tem)[0]);int i2 Integer.parseInt(o2.substring(o2.indexOf(uuid) uuid.length()).split(\\.tem)[0]);return i1 - i2;});//创建空的合并文件,以未见md5为文件名File targetFile new File(fileUploadDir, uuid .mp4);if (!targetFile.getParentFile().exists()) {targetFile.getParentFile().mkdirs();}RandomAccessFile writeFile new RandomAccessFile(targetFile, rw);long position 0;for (String fileName : fileNames) {System.out.println(fileName);File sourceFile new File(fileUploadTempDir / uuid, fileName);RandomAccessFile readFile new RandomAccessFile(sourceFile, rw);int chunksize 1024 * 3;byte[] buf new byte[chunksize];writeFile.seek(position);int byteCount;while ((byteCount readFile.read(buf)) ! -1) {if (byteCount ! chunksize) {byte[] tempBytes new byte[byteCount];System.arraycopy(buf, 0, tempBytes, 0, byteCount);buf tempBytes;}writeFile.write(buf);position position byteCount;}readFile.close();}writeFile.close();cn.hutool.core.io.FileUtil.del(dirFile);}/*** 视频拆分** param inputFilePath D:/home/dxhh/uploadPath/video/md5.mp4* param outputDirectory D:/home/dxhh/uploadPath/video/md5*/Asyncpublic void mp4ToM3u8ForWindow(String fileMd5, String inputFilePath, String outputDirectory, long ms) throws IOException {File uploadFile new File(outputDirectory);if (!uploadFile.exists()) {uploadFile.mkdirs();}Path outputDirPath Paths.get(outputDirectory);//我的ffmpeg.exe放在 项目的/resources/script目录下Path resourcePath Paths.get(./script/ffmpeg.exe);FFmpeg.atPath(resourcePath.getParent()).addInput(UrlInput.fromPath(Paths.get(inputFilePath))).addOutput(UrlOutput.toPath(outputDirPath.resolve(output_%03d.ts))).addArguments(-f, segment).addArguments(-segment_time, ms ms) // 分片时长为30s.addArguments(-segment_list, outputDirPath.resolve(playlist.m3u8).toString()).addArguments(-c:v, copy) // 优化视频编码参数.addArguments(-c:a, copy) // 优化音频编码参数.execute();// 修改生成的m3u8文件将ts链接替换为完整URLupdateM3u8File(fileMd5, outputDirectory);}/*** 视频拆分** param fileMd5 adw1dwdadadwdadasd* param inputFilePath /home/dxhh/uploadPath/video/md5.mp4* param outputDirectory /home/dxhh/uploadPath/video/md5* throws IOException* throws InterruptedException*/public void mp4ToM3u8ForLinux(String fileMd5, String inputFilePath, String outputDirectory, long ms) throws IOException, InterruptedException {String command ffmpeg -i inputFilePath -c copy -map 0 -f segment -segment_time ms ms -segment_list outputDirectory /playlist.m3u8 outputDirectory /output_%03d.ts;//ffmpeg -i /home/dxhh/uploadPath/video/md5.mp4 -c copy -map 0 -f segment -segment_time 1236ms -segment_list /home/dxhh/uploadPath/video/md5/playlist.m3u8 /home/dxhh/uploadPath/video/md5/output_%03d.tslog.info(视频分割脚本{}, command);ProcessBuilder builder new ProcessBuilder(command.split( ));builder.redirectErrorStream(true);Process process builder.start();BufferedReader reader new BufferedReader(new InputStreamReader(process.getInputStream()));String line;while ((line reader.readLine()) ! null) {System.out.println(line);}int exitCode process.waitFor();if (exitCode 0) {System.out.println(FFmpeg command executed successfully);updateM3u8File(fileMd5, outputDirectory);} else {System.out.println(FFmpeg command failed with exit code exitCode);}}private void updateM3u8File(String fileMd5, String outputDirectory) throws IOException {String m3u8FilePath outputDirectory /playlist.m3u8;ListString lines Files.readAllLines(Paths.get(m3u8FilePath));ListString newLines new ArrayList();for (String line : lines) {if (line.endsWith(.ts)) {if (dev.equals(active)) {newLines.add(/dev-api/profile/video/ fileMd5 / line);} else {newLines.add(/stage-api/profile/video/ fileMd5 / line);}} else {newLines.add(line);}}Files.write(Paths.get(m3u8FilePath), newLines);}public long getTsDuration(String filePath) throws EncoderException {int targetSize 2 * 1024 * 1024; // 2MBFile videoFile new File(filePath);long fileSize videoFile.length();Encoder encoder new Encoder();MultimediaInfo multimediaInfo encoder.getInfo(videoFile);long duration multimediaInfo.getDuration();System.out.println(Duration: duration ms);System.out.println(File size: fileSize bytes);// Calculate target duration for a 2MB videolong targetDuration (duration * targetSize) / fileSize;System.out.println(Target duration for a 2MB video: targetDuration ms);return targetDuration;}获取视频时长需要用到jave工具包想上传资源的提示已存在应该可以在csdn搜到 还需要ffmpeg软件如果是windows环境运行只需要调用本地的ffmpeg.exe就好如果是在linux运行需要安装ffmpeg !--视频切割--dependencygroupIdcom.github.kokorin.jaffree/groupIdartifactIdjaffree/artifactIdversion2023.09.10/version/dependencydependencygroupIdit.sauronsoftware.jave/groupIdartifactIdjave2/artifactIdversion1.0.2/versionscopesystem/scopesystemPath${project.basedir}/lib/jave-1.0.2.jar/systemPath/dependency2.3 linux中安装ffmpeg 下载 ffmpeg 工具包并解压 wget http://www.ffmpeg.org/releases/ffmpeg-4.2.tar.gz tar -zxvf ffmpeg-4.2.tar.gz进入工具包文件夹并进行安装将 ffmpeg 安装至 / usr/local/ffmpeg 下 cd ffmpeg-4.2./configure --prefix/usr/local/ffmpeg ./configure --prefix/usr/local/ffmpeg --enable-openssl --disable-x86asm make make install注意若出现以下报错请跳至第五步待第五步安装成功后再返回第二步。 配置环境变量使其 ffmpeg 命令生效 #利用vi编辑环境变量 vi /etc/profile#在最后位置处添加环境变量点击i进入编辑模式esc键可退出编辑模式 export PATH$PATH:/usr/local/ffmpeg/bin#退出编辑模式后:wq 保存退出 #刷新资源使其生效 source /etc/profile查看 ffmpeg 版本验证是否安装成功 ffmpeg -version 若出现以下内容则安装成功。 若第二步出现图片中的错误信息则需要安装 yasm 记得退出 ffmpeg 工具包文件夹cd … 返回上一层 #下载yasm工具包 wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz#解压 tar -zxvf yasm-1.3.0.tar.gz#进入工具包文件夹并开始安装 cd yasm-1.3.0 ./configure make make install安装完成后直接返回第二步即可此时命令就不会报错了。 2.4 视频资源地址 因为是基于若依框架开发的其实只要上传的的时候是往 RuoYiConfig.getProfile() 这个指定配置目录保存文件都是能直接访问不需要额外开发这里就简单过一下 若依的自定义参数配置类从yml文件读取用户配置 Component ConfigurationProperties(prefix xxx) public class RuoYiConfig {/*** 上传路径 /home/user/xxxx/upload*/private static String profile; }在通用配置定义一个静态资源路由前缀 /*** 通用常量定义** author li.dh*/ public class CommonConstant {/*** 资源映射路径 前缀*/public static final String RESOURCE_PREFIX /profile; }在mvc配置中添加静态资源的转发映射将/profile前缀的请求转发到RuoYiConfig.getProfile()路径下 Configuration public class ResourcesConfig implements WebMvcConfigurer {Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {/** 本地文件上传路径 */registry.addResourceHandler(CommonConstant.RESOURCE_PREFIX /**).addResourceLocations(file: RuoYiConfig.getProfile() /);/** swagger配置 */registry.addResourceHandler(/swagger-ui/**).addResourceLocations(classpath:/META-INF/resources/webjars/springfox-swagger-ui/).setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());} }3. vue播放流视频 我的需求是在列表上点击视频弹出播放弹窗 !-- 播放视频 --el-dialog :titletitle :visible.syncopen width800px append-to-body closeopenfalsevideo-player classvideo-player vjs-custom-skinrefvideoPlayer:playsinlinetrue:optionsplayerOptions/video-player/el-dialogimport video.js/dist/video-js.cssdata(){return {// 弹出层标题title: ,m3u8Url: ,// 是否显示弹出层open: false,playerOptions: {playbackRates: [0.5, 1.0, 1.5, 2.0], // 可选的播放速度autoplay: true, // 如果为true,浏览器准备好时开始回放。muted: false, // 默认情况下将会消除任何音频。loop: false, // 是否视频一结束就重新开始。preload: auto, // 建议浏览器在video加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频如果浏览器支持language: zh-CN,aspectRatio: 16:9, // 将播放器置于流畅模式并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字例如16:9或4:3fluid: true, // 当true时Video.js player将拥有流体大小。换句话说它将按比例缩放以适应其容器。sources: [{type: application/x-mpegURL, // 类型src: this.m3u8Url}],poster: , // 封面地址notSupportedMessage: 此视频暂无法播放请稍后再试, // 允许覆盖Video.js无法播放媒体源时显示的默认信息。controlBar: {timeDivider: true, // 当前时间和持续时间的分隔符durationDisplay: true, // 显示持续时间remainingTimeDisplay: false, // 是否显示剩余时间功能fullscreenToggle: true // 是否显示全屏按钮}}} }, methods: {openVideo(picurl, url, title) {this.title titlelet videourl process.env.VUE_APP_BASE_API urllet imgurl process.env.VUE_APP_BASE_API picurl// console.log(视频地址 , videourl)this.m3u8Url videourlthis.playerOptions.sources[0].src videourl // 重新加载视频this.playerOptions.poster imgurl // 封面// this.$refs.videoPlayer.play() // 播放视频this.open true} }4. 实现效果
http://www.pierceye.com/news/217358/

相关文章:

  • 网站建设为什么学flash最新新闻消息事件
  • 高端网站建设需要的人员配备编辑目录中的字体 wordpress
  • 电脑维修网站模板金融商城快捷申请网站模板下载
  • wordpress 本地建站教程化纤公司网站建设
  • 广州网站设计公司新闻给客户做非法网站
  • 微商城手机网站制作公司痞子 wordpress
  • 公司网站备案申请鹤山做网站
  • 南阳那里有做网站的聊城网站优化
  • 网站开发技术实验教程长沙网站托管公司排名
  • 美妆网站建设项目计划书软件开发培训班机构
  • 小视频网站怎么做seo网络优化师
  • 建个门户网站新手学编程用什么软件
  • 旅游网站建设规范wordpress用户注册协议
  • 淘宝客网站女装模板下载wordpress5 没有块引用
  • 35网站建设博客移动端网站模板
  • 卡盟网站建设公司品牌策划ppt
  • 自己如何做网站教程广州建网站有哪些
  • 网站建设 市场规模加强财政门户网站建设工作
  • wordpress 搭建多站点电子商务网站
  • 免费制作网页的网站万网租空间 网站
  • 上海 网站 备案ios开发网站app
  • 网站建设,h5,小程序众安保险
  • 大连网站建设资讯网站seo如何优化
  • 手表网站建设策划西地那非片怎么服用最佳
  • 常德网站设计英文版网站怎么做
  • 权威网站建设网站的工具
  • php手机网站模板厦门网站设计建设
  • 焦作集团网站建设做食品网站需要什么资质
  • 西北电力建设甘肃工程公司网站90设计电商模板
  • 内蒙古网站设计推广网站注册赚佣金