扩展_流媒体_点播

流媒体

流媒体(streaming media)是指将一连串的媒体数据压缩后,经过网上分段发送数据,在网上即时传输影音以供观赏的一种技术与过程,此技术使得数据包得以像流水一样发送;如果不使用此技术,就必须在使用前下载整个媒体文件。
流媒体文件一般定义在bit层次结构,因此流数据包并不一定必须按照字节对齐,虽然通常的媒体文件都是按照这种字节对齐的方式打包的。流媒体的三大操作平台是微软公司、RealNetworks、苹果公司提供的。

点播的方案

1、 播放器通过 http协议从http服务器上下载视频文件进行播放
问题:必须等到视频下载完才可以播放,不支持快进到某个时间点进行播放
2、 播放器通过rtmp协议连接媒体服务器以实时流方式播放视频
使用rtmp协议需要架设媒体服务器,造价高,对于直播多采用此方案。
3、 播放器使用HLS协议连接http服务器(Nginx、Apache等)实现近实时流方式播放视频
HLS协议规定:基于Http协议,视频封装格式为ts,视频的编码格式为H264,音频编码格式为MP3、AAC或者AC-3。

HLS(HTTP Live Streaming)

HLS的工作方式是:将视频拆分成若干ts格式的小文件,通过m3u8格式的索引文件对这些ts小文件建立索引。一般10秒一个ts文件,播放器连接m3u8文件播放,当快进时通过m3u8即可找到对应的索引文件,并去下载对应的ts文件,从而实现快进、快退以近实时 的方式播放视频。
IOS、Android设备、及各大浏览器都支持HLS协议。

音视频编码

音视频编码格式:通过音视频的压缩技术,将视频格式转换成另一种视频格式,通过视频编码实现流媒体的传输。
比如:一个.avi的视频文件原来的编码是a,通过编码后编码格式变为b,音频原来为c,通过编码后变为d。
MPEG系列 (由ISO[国际标准组织机构]下属的MPEG[运动图象专家组]开发 )视频编码方面主要是Mpeg1(vcd用的就是它)、Mpeg2(DVD使用)、Mpeg4(的DVDRIP使用的都是它的变种,如:divx,xvid等)、Mpeg4 AVC(正热门);音频编码方面主要是MPEG Audio Layer 1/2、MPEG Audio Layer 3(大名鼎鼎的mp3)、MPEG-2 AAC 、MPEG-4 AAC等等。注意:DVD音频没有采用Mpeg的。

FFmpeg

多媒体视频处理工具FFmpeg有非常强大的功能包括视频采集功能、视频格式转换、视频抓图、给视频加水印等。
下载:FFmpeg https://www.ffmpeg.org/download.html#build-windows

FFmpeg使用

(1)将lucene.avi文件转成mp4

ffmpeg -i lucene.avi lucene.mp4

(2)将lucene.avi文件转成mp3

ffmpeg -i lucene.avi lucene.mp3

(3)将lucene.avi文件转成git

ffmpeg -i lucene.avi lucene.gif

使用ffmpeg生成 m3u8

(1)将avi视频转成mp4

ffmpeg.exe ‐i lucene.avi ‐c:v libx264 ‐s 1280x720 ‐pix_fmt yuv420p ‐b:a 63k ‐b:v 753k ‐r 18 .\lucene.mp4

备注:
-c:v 视频编码为x264 ,x264编码是H264的一种开源编码格式。
-s 设置分辨率。
-pix_fmt yuv420p:设置像素采样方式,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0,它的作用是根据采样方式来从码流中还原每个像素点的YUV(亮度信息与色彩信息)值。
-b 设置码率,-b:a和-b:v分别表示音频的码率和视频的码率,-b表示音频加视频的总码率。码率对一个视频质量有很大的作用。
-r:帧率,表示每秒更新图像画面的次数,通常大于24肉眼就没有连贯与停顿的感觉了。

(2)将mp4生成m3u8

ffmpeg ‐i lucene.mp4 ‐hls_time 10 ‐hls_list_size 0 ‐hls_segment_filename ./hls/lucene_%05d.ts ./hls/lucene.m3u8

备注:
-hls_time 设置每片的长度,单位为秒
-hls_list_size n: 保存的分片的数量,设置为0表示保存所有分片
-hls_segment_filename :段文件的名称,%05d表示5位数字

生成的效果是:将lucene.mp4视频文件每10秒生成一个ts文件,最后生成一个m3u8文件,m3u8文件是ts的索引文件。

(3)VLC打开m3u8文件
使用VLC打开m3u8文件,测试播放效果,VLC 是一款自由、开源的跨平台多媒体播放器及框架,可播放大多数多媒体文件,以及 DVD、音频 CD、VCD 及各类流媒体协议。(http://www.videolan.org/)

码率的设置

码率又叫比特率即每秒传输的bit数,单位为bps(Bit Per Second),码率越大传送数据的速度越快。
码率的计算公式是:文件大小(转成bit)/ 时长(秒)/1024 = kbps 即每秒传输千位数。
例如一个1M的视频,它的时长是10s,它的码率等于 110241024*8/10/1024 = 819Kbps。

如果要将视频上传到优酷则必须按照上面的要求,如果是自己搭建视频服务器,码率设置不易过大,最终达到的视频清晰度满足业务需求即可。

播放器

在web应用中常用的播放器
(1)flash播放器:缺点是需要在客户机安装Adobe Flash Player播放器,优点是flash播放器已经很成熟了,并且浏览器对flash支持也很好。
(2)H5播放器:基于h5自带video标签进行构建,优点是大部分浏览器支持H5,不用再安装第三方的flash播放器,并且随着前端技术的发展,h5技术会越来越成熟。

H5播放器-Video.js

Video.js是一款基于HTML5世界的网络视频播放器。它支持HTML5和Flash视频,它支持在台式机和移动设备上播放视频。这个项目于2010年中开始,目前已在40万网站使用。
官方地址:http://videojs.co

视频服务器

upstream video_server_pool{
	server 192.168.100.2:90 weight=10;
	server 192.168.100.3:90 weight=10;
}
map $http_origin $origin_list {
	default http://www.pingruan.com;
	"~http://www.pingruan.com"  http://www.pingruan.com;
	"~http://ucenter.pingruan.com"  http://ucenter.pingruan.com;	
}
server{
	listen	90;
	server_name	video.pingruan.com;
	location	/video {
		proxy_pass	http://video_server_pool;
		add_header Access-Control-Allow-Origin	$origin_list; #允许跨域访问的外域地址
		# add_header Access-Control-Allow-Origin  *;
		add_header Access-Control-Allow-Credentials	true; #允许客户端携带证书访问
		add_header Access-Control-Allow-Methods  GET; #允许客户端跨域访问的方法
	}
}


server{
	listen	90;
	server_name	localhost;
	location	/video/{
		alias		/develop/video/;
	}
}
server{
	listen	90;
	server_name	localhost;
	location	/video/{
		alias		E:/develop/video/;
	}
}

播放页面Demo

<!DOCTYPE html>
<html lang="en">
<head>
	<meta http‐equiv="content‐type" content="text/html; charset=utf‐8" />
	<title>视频播放</title>
	<link href="/plugins/videojs/video‐js.css" rel="stylesheet">
</head>
<body>
	<video id=example‐video width=800 height=600 class="video‐js vjs‐default‐skin vjs‐big‐play‐centered" controls poster="http://127.0.0.1:90/video/add.jpg">
		<source src="http://video.xuecheng.com/video/hls/lucene.m3u8" type="application/x‐mpegURL">
	</video>
	<input type="button" onClick="switchvideo()" value="switch"/>
	<script src="/plugins/videojs/video.js"></script>
	<script src="/plugins/videojs/videojs‐contrib‐hls.js"></script>
<script>
	var player = videojs('example‐video');
	//player.play();
	//切换视频
	function switchvideo(){
			player.src({
			src: 'http://video.xuecheng.com/video/hls/lucene.m3u8',
			type: 'application/x‐mpegURL',
			withCredentials: true
		});
		player.play();
	}
</script>
</body>
</html>

断点续传

前段

WebUploader
官方:http://fex.baidu.com/webuploader/document.html
在这里插入图片描述

<script>
  import $ from '../../../../static/plugins/jquery/dist/jquery.js'
  import webuploader from '../../../../static/plugins/webuploader/dist/webuploader.js'
  import '../../../../static/css/webuploader/webuploader.css'
  export default{
    data(){
      return{
        uploader:{},
        uploadFile:{},
        percentage:0,
        fileMd5:''
      }
    },
    methods:{
      //开始上传
      upload(){
        if(this.uploadFile && this.uploadFile.id){
          this.uploader.upload(this.uploadFile.id);
        }else{
          alert("请选择文件");
        }
      }
    },
    mounted(){
//      var fileMd5;
//      var uploadFile;
      WebUploader.Uploader.register({
          "before-send-file":"beforeSendFile",
          "before-send":"beforeSend",
          "after-send-file":"afterSendFile"
        },{
          beforeSendFile:function(file) {
            // 创建一个deffered,用于通知是否完成操作
            var deferred = WebUploader.Deferred();
            // 计算文件的唯一标识,用于断点续传
            (new WebUploader.Uploader()).md5File(file, 0, 100*1024*1024)
              .then(function(val) {

                this.fileMd5 = val;
                this.uploadFile = file;
//                alert(this.fileMd5 )
                //向服务端请求注册上传文件
                $.ajax(
                  {
                    type:"POST",
                    url:"/api/media/upload/register",
                    data:{
                      // 文件唯一表示
                      fileMd5:this.fileMd5,
                      fileName: file.name,
                      fileSize:file.size,
                      mimetype:file.type,
                      fileExt:file.ext
                    },
                    dataType:"json",
                    success:function(response) {
                      if(response.success) {
                        //alert('上传文件注册成功开始上传');
                        deferred.resolve();
                      } else {
                        alert(response.message);
                        deferred.reject();
                      }
                    }
                  }
                );
              }.bind(this));

            return deferred.promise();
          }.bind(this),
          beforeSend:function(block) {
            var deferred = WebUploader.Deferred();
            // 每次上传分块前校验分块,如果已存在分块则不再上传,达到断点续传的目的
            $.ajax(
              {
                type:"POST",
                url:"/api/media/upload/checkchunk",
                data:{
                  // 文件唯一表示
                  fileMd5:this.fileMd5,
                  // 当前分块下标
                  chunk:block.chunk,
                  // 当前分块大小
                  chunkSize:block.end-block.start
                },
                dataType:"json",
                success:function(response) {
                  if(response.fileExist) {
                    // 分块存在,跳过该分块
                    deferred.reject();
                  } else {
                    // 分块不存在或不完整,重新发送
                    deferred.resolve();
                  }
                }
              }
            );
            //构建fileMd5参数,上传分块时带上fileMd5
            this.uploader.options.formData.fileMd5 = this.fileMd5;
            this.uploader.options.formData.chunk = block.chunk;
            return deferred.promise();
          }.bind(this),
          afterSendFile:function(file) {
            // 合并分块
            $.ajax(
              {
                type:"POST",
                url:"/api/media/upload/mergechunks",
                data:{
                  fileMd5:this.fileMd5,
                  fileName: file.name,
                  fileSize:file.size,
                  mimetype:file.type,
                  fileExt:file.ext
                },
                success:function(response){
                  //在这里解析合并成功结果
                  if(response && response.success){
                      alert("上传成功")
                  }else{
                      alert("上传失败")
                  }
                }
              }
            );
          }.bind(this)
        }
      );
      // 创建uploader对象,配置参数
      this.uploader = WebUploader.create(
        {
          swf:"/static/plugins/webuploader/dist/Uploader.swf",//上传文件的flash文件,浏览器不支持h5时启动flash
          server:"/api/media/upload/uploadchunk",//上传分块的服务端地址,注意跨域问题
          fileVal:"file",//文件上传域的name
          pick:"#picker",//指定选择文件的按钮容器
          auto:false,//手动触发上传
          disableGlobalDnd:true,//禁掉整个页面的拖拽功能
          chunked:true,// 是否分块上传
          chunkSize:1*1024*1024, // 分块大小(默认5M)
          threads:3, // 开启多个线程(默认3个)
          prepareNextFile:true// 允许在文件传输时提前把下一个文件准备好
        }
      );

      // 将文件添加到队列
      this.uploader.on("fileQueued", function(file) {
          this.uploadFile = file;
          this.percentage = 0;

        }.bind(this)
      );
      //选择文件后触发
      this.uploader.on("beforeFileQueued", function(file) {
//     this.uploader.removeFile(file)
        //重置uploader
        this.uploader.reset()
        this.percentage = 0;
      }.bind(this));

      // 监控上传进度
      // percentage:代表上传文件的百分比
      this.uploader.on("uploadProgress", function(file, percentage) {
          this.percentage = Math.ceil(percentage * 100);
      }.bind(this));
      //上传失败触发
      this.uploader.on("uploadError", function(file,reason) {
        console.log(reason)
        alert("上传文件失败");
      });
      //上传成功触发
      this.uploader.on("uploadSuccess", function(file,response ) {
        console.log(response)
//        alert("上传文件成功!");
      });
      //每个分块上传请求后触发
      this.uploader.on( 'uploadAccept', function( file, response ) {
          if(!(response && response.success)){//分块上传失败,返回false
              return false;
          }
      });

    }
  }

</script>

后端

业务流程

服务端需要实现如下功能:
1、上传前检查上传环境
检查文件是否上传,已上传则直接返回。
检查文件上传路径是否存在,不存在则创建。
2、分块检查
检查分块文件是否上传,已上传则返回true。
未上传则检查上传路径是否存在,不存在则创建。
3、分块上传
将分块文件上传到指定的路径。
4、合并分块
将所有分块文件合并为一个文件。
在数据库记录文件信息。

Entity

@Data
@ToString
@Document(collection = "media_file")
public class MediaFile {
	/*
	文件id、名称、大小、文件类型、文件状态(未上传、上传完成、上传失败)、上传时间、视频处理方式、视频处
	理状态、hls_m3u8,hls_ts_list、课程视频信息(课程id、章节id)
	*/
	@Id
	//文件id
	private String fileId;
	//文件名称
	private String fileName;
	//文件原始名称
	private String fileOriginalName;
	//文件路径
	private String filePath;
	//文件url
	private String fileUrl;
	//文件类型
	private String fileType;
	//mimetype
	private String mimeType;
	//文件大小
	private Long fileSize;
	//文件状态
	private String fileStatus;
	//上传时间
	private Date uploadTime;
}

Dao

public interface MediaFileRepository extends MongoRepository<MediaFile,String> {
}

Service

@Service
public class MediaUploadService {
	private final static Logger LOGGER = LoggerFactory.getLogger(MediaUploadController.class);
	@Autowired
	MediaFileRepository mediaFileRepository;
	//上传文件根目录
	@Value("${xc‐service‐manage‐media.upload‐location}")
	String uploadPath;
	/**
	* 根据文件md5得到文件路径
	* 规则:
	* 一级目录:md5的第一个字符
	* 二级目录:md5的第二个字符
	* 三级目录:md5
	* 文件名:md5+文件扩展名
	* @param fileMd5 文件md5值
	* @param fileExt 文件扩展名
	* @return 文件路径
	*/
	private String getFilePath(String fileMd5,String fileExt){
		String filePath = uploadPath+fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) +
		"/" + fileMd5 + "/" + fileMd5 + "." + fileExt;
		return filePath;
	}
	//得到文件目录相对路径,路径中去掉根目录
	private String getFileFolderRelativePath(String fileMd5,String fileExt){
		String filePath = fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" +
		fileMd5 + "/";
		return filePath;
	}
	//得到文件所在目录
	private String getFileFolderPath(String fileMd5){
		String fileFolderPath = uploadPath+ fileMd5.substring(0, 1) + "/" + fileMd5.substring(1,
		2) + "/" + fileMd5 + "/" ;
		return fileFolderPath;
	}
	//创建文件目录
	private boolean createFileFold(String fileMd5){
		//创建上传文件目录
		String fileFolderPath = getFileFolderPath(fileMd5);
		File fileFolder = new File(fileFolderPath);
		if (!fileFolder.exists()) {
			//创建文件夹
			boolean mkdirs = fileFolder.mkdirs();
			return mkdirs;
		}
		return true;
	}
	//文件上传注册
	public ResponseResult register(String fileMd5, String fileName, String fileSize, String
	mimetype, String fileExt) {
		//检查文件是否上传
		//1、得到文件的路径
		String filePath = getFilePath(fileMd5, fileExt);
		File file = new File(filePath);
		//2、查询数据库文件是否存在
		Optional<MediaFile> optional = mediaFileRepository.findById(fileMd5);
		//文件存在直接返回
		if(file.exists() && optional.isPresent()){
			ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_EXIST);
		}
		boolean fileFold = createFileFold(fileMd5);
		if(!fileFold){
			//上传文件目录创建失败
			ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_CREATEFOLDER_FAIL);
		}
		return new ResponseResult(CommonCode.SUCCESS);
	}
	//分片检查----得到块文件所在目录
	private String getChunkFileFolderPath(String fileMd5){
		String fileChunkFolderPath = getFileFolderPath(fileMd5) +"/" + "chunks" + "/";
		return fileChunkFolderPath;
	}
	//分片检查----检查块文件
	public CheckChunkResult checkchunk(String fileMd5, String chunk, String chunkSize) {
		//得到块文件所在路径
		String chunkfileFolderPath = getChunkFileFolderPath(fileMd5);
		//块文件的文件名称以1,2,3..序号命名,没有扩展名
		File chunkFile = new File(chunkfileFolderPath+chunk);
		if(chunkFile.exists()){
		return new CheckChunkResult(MediaCode.CHUNK_FILE_EXIST_CHECK,true);
		}else{
		return new CheckChunkResult(MediaCode.CHUNK_FILE_EXIST_CHECK,false);
	}
	//上传分块-----块文件上传
	public ResponseResult uploadchunk(MultipartFile file, String fileMd5, String chunk) {
		if(file == null){
			ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_ISNULL);
		}
		//创建块文件目录
		boolean fileFold = createChunkFileFolder(fileMd5);
		//块文件
		File chunkfile = new File(getChunkFileFolderPath(fileMd5) + chunk);
		//上传的块文件
		InputStream inputStream= null;
		FileOutputStream outputStream = null;
		try {
			inputStream = file.getInputStream();
			outputStream = new FileOutputStream(chunkfile);
			IOUtils.copy(inputStream,outputStream);
		} catch (Exception e) {
			e.printStackTrace();
			LOGGER.error("upload chunk file fail:{}",e.getMessage());
			ExceptionCast.cast(MediaCode.CHUNK_FILE_UPLOAD_FAIL);
		}finally {
			try {
				inputStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				outputStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return new ResponseResult(CommonCode.SUCCESS);
	}
	//上传分块-----创建块文件目录
	private boolean createChunkFileFolder(String fileMd5){
		//创建上传文件目录
		String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
		File chunkFileFolder = new File(chunkFileFolderPath);
		if (!chunkFileFolder.exists()) {
			//创建文件夹
			boolean mkdirs = chunkFileFolder.mkdirs();
			return mkdirs;
		}
		return true;
	}
	//合并块------文件
	public ResponseResult mergechunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
		//获取块文件的路径
		String chunkfileFolderPath = getChunkFileFolderPath(fileMd5);
		File chunkfileFolder = new File(chunkfileFolderPath);
		if(!chunkfileFolder.exists()){
			chunkfileFolder.mkdirs();
		}
		//合并文件路径
		File mergeFile = new File(getFilePath(fileMd5,fileExt));
		//创建合并文件
		//合并文件存在先删除再创建
		if(mergeFile.exists()){
			mergeFile.delete();
		}
		boolean newFile = false;
		try {
			newFile = mergeFile.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
			LOGGER.error("mergechunks..create mergeFile fail:{}",e.getMessage());
		}
		if(!newFile){
			ExceptionCast.cast(MediaCode.MERGE_FILE_CREATEFAIL);
		}
		//获取块文件,此列表是已经排好序的列表
		List<File> chunkFiles = getChunkFiles(chunkfileFolder);
		//合并文件
		mergeFile = mergeFile(mergeFile, chunkFiles);
		if(mergeFile == null){
			ExceptionCast.cast(MediaCode.MERGE_FILE_FAIL);
		}
		//校验文件
		boolean checkResult = this.checkFileMd5(mergeFile, fileMd5);
		if(!checkResult){
			ExceptionCast.cast(MediaCode.MERGE_FILE_CHECKFAIL);
		}
		//将文件信息保存到数据库
		MediaFile mediaFile = new MediaFile();
		mediaFile.setFileId(fileMd5);
		mediaFile.setFileName(fileMd5+"."+fileExt);
		mediaFile.setFileOriginalName(fileName);
		//文件路径保存相对路径
		mediaFile.setFilePath(getFileFolderRelativePath(fileMd5,fileExt));
		mediaFile.setFileSize(fileSize);
		mediaFile.setUploadTime(new Date());
		mediaFile.setMimeType(mimetype);
		mediaFile.setFileType(fileExt);
		//状态为上传成功
		mediaFile.setFileStatus("301002");
		MediaFile save = mediaFileDao.save(mediaFile);
		return new ResponseResult(CommonCode.SUCCESS);
	}
	//合并块------校验文件的md5值
	private boolean checkFileMd5(File mergeFile,String md5){
		if(mergeFile == null || StringUtils.isEmpty(md5)){
		return false;
	}
	//合并块------进行md5校验
	FileInputStream mergeFileInputstream = null;
		try {
			mergeFileInputstream = new FileInputStream(mergeFile);
			//得到文件的md5
			String mergeFileMd5 = DigestUtils.md5Hex(mergeFileInputstream);
			//比较md5
			if(md5.equalsIgnoreCase(mergeFileMd5)){
				return true;
			}
		} catch (Exception e) {
			e.printStackTrace();
			LOGGER.error("checkFileMd5 error,file is:{},md5 is:
			{}",mergeFile.getAbsoluteFile(),md5);
		}finally{
			try {
				mergeFileInputstream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return false;
	}
	//合并块------获取所有块文件
	private List<File> getChunkFiles(File chunkfileFolder){
		//获取路径下的所有块文件
		File[] chunkFiles = chunkfileFolder.listFiles();
		//将文件数组转成list,并排序
		List<File> chunkFileList = new ArrayList<File>();
		chunkFileList.addAll(Arrays.asList(chunkFiles));
		//排序
		Collections.sort(chunkFileList, new Comparator<File>() {
				@Override
				public int compare(File o1, File o2) {
					if(Integer.parseInt(o1.getName())>Integer.parseInt(o2.getName())){
					return 1;
				}
					return ‐1;
				}
			});
		return chunkFileList;
	}
	//合并块------合并文件
	private File mergeFile(File mergeFile,List<File> chunkFiles){
		try {
			//创建写文件对象
			RandomAccessFile raf_write = new RandomAccessFile(mergeFile,"rw");
			//遍历分块文件开始合并
			//读取文件缓冲区
			byte[] b = new byte[1024];
			for(File chunkFile:chunkFiles){
				RandomAccessFile raf_read = new RandomAccessFile(chunkFile,"r");
				int len = ‐1;
				//读取分块文件
				while((len = raf_read.read(b))!=‐1){
					//向合并文件中写数据
					raf_write.write(b,0,len);
				}
				raf_read.close();
			}
			raf_write.close();
		} catch (Exception e) {
			e.printStackTrace();
			LOGGER.error("merge file error:{}",e.getMessage());
			return null;
		}
		return mergeFile;
	}
}

Controller

@RestController
@RequestMapping("/media/upload")
public class MediaUploadController implements MediaUploadControllerApi {
	@Autowired
	MediaUploadService mediaUploadService;
	@Override
	@PostMapping("/register")
	public ResponseResult register(@RequestParam("fileMd5") String fileMd5,
    	@RequestParam("fileName") String fileName, @RequestParam("fileSize") Long fileSize,
    	@RequestParam("mimetype") String mimetype, @RequestParam("fileExt") String fileExt) {
			return mediaUploadService.register(fileMd5,fileName,fileSize,mimetype,fileExt);
	}
	@Override
	@PostMapping("/checkchunk")
	public CheckChunkResult checkchunk(@RequestParam("fileMd5") String fileMd5,
    	@RequestParam("chunk") Integer chunk, @RequestParam("chunkSize") Integer chunkSize) {
    	return mediaUploadService.checkchunk(fileMd5,chunk,chunkSize);
	}
	@Override
	@PostMapping("/uploadchunk")
	public ResponseResult uploadchunk(@RequestParam("file") MultipartFile file,
    	@RequestParam("fileMd5") String fileMd5, @RequestParam("chunk") Integer chunk) {
    	return mediaUploadService.uploadchunk(file,fileMd5,chunk);
	}
	@Override
	@PostMapping("/mergechunks")
	public ResponseResult mergechunks(@RequestParam("fileMd5") String fileMd5,
    	@RequestParam("fileName") String fileName, @RequestParam("fileSize") Long fileSize,
    	@RequestParam("mimetype") String mimetype, @RequestParam("fileExt") String fileExt) {
    	return mediaUploadService.mergechunks(fileMd5,fileName,fileSize,mimetype,fileExt);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值