HTTP视频上传(断点续传)

本文档详细介绍了使用“HTTP断点续传”的方式实现文件上传的对接说明,并在本小节最后给出前端HTML5实现demo,以及若干服务端实现demo。

1 创建视频上传信息

根据本地待上传文件的各种属性请求该接口,获取系统分配的视频id(videoid), 上传路径(metaurl,chunkurl)等上传信息,以便进行后续上传。本接口需要使用THQS方式进行请求参数校验(关于THQS算法的细节请参见Spark API附录I), 本接口不支持跨域访问。

接口地址http://spark.bokecc.com/api/video/create/v2

接口类型: GET 请求

需要传递以下参数:

参数 含义
userid 用户id,必选
title 视频标题
tag 视频标签
description 视频描述
categoryid 分类id(不选 默认上传到用户的默认分类)
filename 视频文件名(必须带后缀名,如不带 默认为视频文件), 必选
filesize 视频文件大小(Byte), 必选
notify_url 视频处理完毕的通知地址

返回数据uploadinfo包括如下字段:

字段名 含义
videoid 视频id
userid 用户id
servicetype 服务类型
metaurl 系统分配的文件状态以及断点位置查询接口
chunkurl 系统分配的文件内容块上传接口

返回json格式信息如下:

"uploadinfo": {

        "videoid": "3B35ED608474B8DB2BBA",

"userid":“40EF004A6F",

"servicetype":“240593F089",

"metaurl":"http://abc.com/servlet/uploadmeta/v2",

"chunkurl":"http://abc.com/servlet/uploadchunk/v2"

   }

}

异常返回格式:

{

"error":"SERVICE_EXPIRED"

}

错误码的定义如下表:

错误码 含义
INVALID_REQUEST 用户输入参数错误
SPACE_NOT_ENOUGH 用户剩余空间不足
SERVICE_EXPIRED 用户服务终止
PROCESS_FAIL 服务器处理失败
TOO_MANY_REQUEST 访问过于频繁
PERMISSION_DENY 用户服务无权限

2 查询文件上传状态及断点位置

可以通过该接口查询文件的上传状态及“断点位置”(上传服务器已经接收的文件大小), 处于上传状态中的文件记录,可以调用servlet/uploadchunk/v2 接口(即chunkurl, 后续有详细介绍) 从“断点位置”处发送文件块内容,实现“断点续传”。需要说明的是,在发送第一个文件块之前,必须要调用本接口一次,检查该条文件记录是否处于上传状态中。文件状态及“断点”介绍请见本接口的响应说明。

本接口支持跨域访问,不需要进行THQS参数加密校验。

接口地址:http://abc.bokecc.com/servlet/uploadmeta/v2

接口类型: GET请求。

(接口路径即上一步请求api/video/create/v2返回上传信息中的metaurl。)

需要传递以下参数:

参数 含义
uid 用户id,必选
ccvid 视频文件vid,必选
filename 视频名称,必选
md5 视频文件md5
32位md5串;如果请求时填写了md5的话,会在文件接收完成之后,校验文件的完整性;否则,不做完整性校验。
计算并校验md5可能会比较耗时,我们建议在后端程序实现的上传对接中校验md5,前端flash或H5实现时可以不做md5校验。
filesize 视频文件大小,单位Byte,必选
servicetype 服务类型,必选

返回数据 包括如下字段:

字段名 含义
result 响应状态码:(1,0,-1,-2,-3),响应状态码代表了当前的文件状态
1 文件已全部接收,上传成功;
0 文件仍在上传状态中,成功返回“断点位置”;
-1 上传失败,可以放弃“本次”上传,不要重试了;
-2 服务器内部错误,详见msg信息,可以续传重试;
-3 请求参数错误,详见msg信息,请修正错误后重试
msg 错误提示,出错时给出异常信息。
received 单位Byte,上传服务器已接收文件长度,亦即“断点位置”。

返回json格式信息如下:

{

       "result": 0,

"msg":"ok",

"received":1048576

}

上面的返回代表“查询状态及断点成功,已上传1048576字节,请继续上传”。

异常返回格式

{

"result": -3,

"msg":“Null or invalid filesize”,

"received":-1

}

上面的返回代表“请求参数错误,参数filesize非法或者为空”。

当返回result=1,此时received=文件长度filesize,代表整个文件已经完全接受完毕,上传操作可以结束了。

3 上传视频文件块CHUNK

通过该接口,向服务器分块发送文件数据。最大文件块chunksize=4MB。在上传具体的文件内容之前,要根据当前的”断点位置“对文件进行分块,然后依次调用此接口顺序上传文件块(即文件块的起始索引要和服务器已接收的长度相吻合)。如果传输过程中发生异常中断,需要再次获取”断点位置“并重新分块上传。

本接口支持跨域,不需要进行THQS参数校验。

接口地址: http://abc.bokecc.com/servlet/uploadchunk/v2

接口类型: POST 请求。 (接口路径即请求api/video/create/v2返回上传信息中的chunkurl。)

需要传递以下参数:

|参数|含义| |ccvid|vid,视频id, 必选|

参数ccvid直接附带在chunkurl之后即可,例如:http://abc.bokecc.com/servlet/uploadchunk/v2?ccvid=3B35ED608474B8DB2BB

返回数据 包括如下字段:

字段 含义
result 响应状态码:(1,0,-1,-2,-3),响应状态码代表了当前的文件状态
1 文件已全部接收,上传成功;
0 成功接收文件块,并返回当前的“断点位置”;
-1 上传失败,可以放弃“本次”上传,不要重试了;
-2 服务器内部错误,详见msg信息,可以续传重试;
-3 请求参数错误,详见msg信息,请修正错误后重试
msg 错误提示,出错时给出异常信息。
received 单位Byte,上传服务器已接收文件长度,亦即“断点位置”。

返回json格式信息如下:

{

       "result": 1,

"msg":"Receive success",

"received":128141777

}

上面的返回表示原始文件大小是 128141777 Bytes,服务器端接收了 128141777 Bytes,文件上传成功。

异常返回格式

{

        "result": -2,

"msg":"Internal IO error",

"received":-1

}

上面的返回代表”服务器内部IO错误“,当发生服务器内部错误的时候,可实现重试续传逻辑,以提高上传成功率。
当返回result=1,此时received=文件长度filesize,代表整个文件已经完全接受完毕,上传操作可以结束了。

4 上传过程中相关问题说明

(1) “HTTP断点续传”实现机制:

本接口通过HTTP POST方式将本地文件的指定部分(一段二进制数据)发送到服务器,服务器端会返回已经接收的文件长度,即”断点位置“。将要发送文件块的起止位置和”断点位置“的对应关系要符合HTTP/1.1协议(RFC 7233, section 4.2 :

https://tools.ietf.org/html/rfc7233#section-4.2的Content-Range约定。

POST报文格式:

POST  /servlet/resumechunk?ccvid=3B35ED608474B8DB2BB   HTTP/1.1

user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4)

Charsert: UTF-8

Content-Type: multipart/form-data;

boundary=---------CCHTTPAPIFormBoundaryEEXX33781

Content-Range: bytes 0-1048575/128141777

Cache-Control: no-cache

Host: abc.bokecc.com

Accept: text/*

Connection: keep-alive

Content-Length: 1048773



-----------CCHTTPAPIFormBoundaryEEXX33781

Content-Disposition: form-data;name="file";filename="local.mp4"

Content-Type: application/octet-strea

(... binary block …)
-----------CCHTTPAPIFormBoundaryEEXX33781--

(2)Content-Range与断点说明:

用户在发送文件块的时候要根据返回的“断点位置”来设置HTTP header中的Content-Range内容。

Content-Range: bytes x-y/z

x 代表该块数据在整个文件中的开始索引== 上次请求返回的“断点位置”(received)。

y 代表该块数据在整个文件中的结束索引。

z代表文件总长度,即z = filesize 。

X和y都表示文件索引的下标位置,下标从0开始,到z-1止。即0 <= x <=y<=z-1 ,y-x+1=本次文件块的大小。

再举例说明一下:像上面z=filesize=128141777字节,大概是122.2M,每次发送1M内容的话,需要连续发送123次。

第一次:Content-Range: bytes 0-1048575/128141777

第二次:Content-Range: bytes 1048576-2097151/128141777

第三次:Content-Range: bytes 2097152-3145727/128141777

...

假设发送第n块的时候由于网络原因产生中断,再次GET servlet/uploadmeta/v2返回的断点位置是127083985,那么

第n+1次:Content-Range: bytes 127083985-128132560/128141777

第n+2次:Content-Range: bytes 128132561-128141776/128141777

发送完毕,最后一块发送了128141776- 128132561+1=9216字节。

(3)Content-Type与boundary说明:

在POST请求中上传文件内容,Content-Type 格式如下:

Content-Type: multipart/form-data; boundary=boundaryText

body体内容格式如下:

--boundaryText\r\n

Content-Disposition: form-data;name="file";filename="local.mp4"\r\n

Content-Type: application/octet-stream\r\n

\r\n

(... binary block ...)\r\n

--boundaryText--\r\n

其中,(... binary block …) 部分为文件块二进制数据内容。详细说明请参看rfc1867( Form-based File Upload: https://www.ietf.org/rfc/rfc1867.txt

发送文件块POST请求java代码示例:

/* url为http//abc.bokecc.com/servlet/uploadchunk/v2?ccvid=videoid*/

/* chunkStart为本次文件块的开始索引*/

/* chunkEnd为本次文件块的结束索引*/

/* file为本地待上传文件*/

/* bufferOut为本次文件块内容的二进制数组,bufferOut.length=chunkEnd-chunkStart+1 */

public  String uploadchunk(String url, long chunkStart, long chunkEnd, File file, byte[] bufferOut) {

         HttpURLConnection conn = null;

try{

// 定义数据分隔线

               String BOUNDARY = "---------CCHTTPAPIFormBoundaryEEXX"+ newRandom().nextInt(65536);

               URL openUrl = newURL(url);conn = (HttpURLConnection)openUrl.openConnection();

// 发送POST请求必须设置如下两行

               conn.setDoOutput(true);

               conn.setDoInput(true);

               conn.setUseCaches(false);

               conn.setRequestMethod("POST");

               conn.setRequestProperty("connection", "Keep-Alive");

               conn.setRequestProperty("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4)");

               conn.setRequestProperty("Charsert", "UTF-8");

               conn.setRequestProperty("Content-Type", "multipart/form-data; boundary="+ BOUNDARY);

// content-range

              conn.setRequestProperty("Content-Range", "bytes "+ chunkStart + "-"+ chunkEnd + "/"+ file.length());

               OutputStream out = newDataOutputStream(conn.getOutputStream());

               StringBuilder sb = newStringBuilder();

              sb.append("--").append(BOUNDARY).append("\r\n");

               sb.append("Content-Disposition: form-data;name=\"file\";filename=\""+ file.getName()+ "\"\r\n");

               sb.append("Content-Type: application/octet-stream\r\n");

              sb.append("\r\n");

byte[] data = sb.toString().getBytes("UTF-8");

               out.write(data);

               out.write(bufferOut);

// 定义最后数据分隔线

byte[] end_data = ("\r\n--"+ BOUNDARY + "--\r\n").getBytes();

               out.write(end_data);

               out.flush();

               out.close();


// 定义BufferedReader输入流来读取URL的响应

               BufferedReader reader = newBufferedReader(newInputStreamReader(conn.getInputStream()));

               StringBuffer resultBuf = newStringBuffer("");

               String line = null;

while((line = reader.readLine()) != null) {

               resultBuf.append(line);

               }

               reader.close();

               conn.disconnect();

returnresultBuf.toString();

        } catch(Exception e) {

               System.out.println("发送POST请求出现异常!"+ e);

               e.printStackTrace();

        }finally{

if(conn != null) conn.disconnect();

        }

return null;

 }

(5) 上传步骤说明(流程图):

1、调用api/video/create/v2, 获取视频上传信息(videoid,metaurl,chunkurl等)。

2、调用servlet/uploadmeta/v2 (metaurl), 查询该条文件的上传状态及断点位置。

3、根据断点位置对本地文件进行分块,循环调用servlet/uploadchunk/v2 (chunkurl) 发送文件块,直到文件数据全部发送。

4、如果在发送文件块的过程中出现中断,可再次调用servlet/uploadmeta/v2 (metaurl) 以查询当前的文件状态及断点位置,判断并执行后续动作,直到文件上传成功或失败。

5、应当实现适宜的重试循环逻辑,以提高上传成功率(当网络环境较差时,可能会多次出现上传中断或拿不到响应的情况,或者遇到服务器内部错误的时候,应该重试续传,具体可参看流程图)

前端H5实现上传demo地址: https://github.com/CCVideo/VOD_H5_UploadDemo

服务端Java实现的上传demo地址:https://github.com/CCVideo/VOD_JAVA_UploadDemo

results matching ""

    No results matching ""