当前位置:   article > 正文

抖音开放平台-视频切片-视频分片上传-不合法的参数ID-不合法的对象ID_抖音开放平台分片上传

抖音开放平台分片上传

问题描述

1、最近遇到个问题,做业务需要管理几个抖音账号,用抖音开放平台做分片上传视频,多次返回不合法参数id,提交工单之后给的回复没有任何参考价值。
2、例如视频文件按15M进行切片,调用分片上传初始化接口,获取一个upload-id,执行上传;我只上传第一个视频观察返回信息。第一次第一个切片执行上传,返回不合法参数id,继续执行第二个切片的上传,还是不合法参数id,之后的切片就不用考虑了;第二次执行切片上传,重新获取upload-id,上传第一切片,发现居然上传成功了,紧接着尝试上传其他切片,都能成功;第三次执行上传,重新获取upload-id,上传第一个切片,发现又成了不合法参数id,后续的其他切片都上传失败。

工单信息

工单状态

已解决
工单说明
同样的上传参数,39M的视频分片上传,15M+24M切分,第一次上传失败,20210810XXXXXXXXXXXXXXX4927233640,不合法的对象id,但是我再操作一次就能上传成功了,2021081XXXXXXXXXXXXXXX452192B2343BD;紧接着进行第三次上传,又出现了问题202108XXXXXXXXXXXX12059196332065FA;请问相同的文件,为什么能有成功的情况?不合法的对象id到底指的是什么?

2021年08月10日 21:32

工单回复:

您好,单片建议大小在20M以内,太大会导致传输不稳定
在这里插入图片描述

这工单的回复完全没有什么参考意义,等这个工单的处理就3天时间,期间一直不断尝试,一直也没有解决,失败的频率很高

问题解决

1

1、尝试用10M大小的文件切片,偶然间右击查看文件属性,大小10M,占用空间12M,怀疑切片有问题。1024X1024X10,按这个大小进行文件切片,切出来的文件占用空间12M,采用其他的方法进行切片,1000X1000X10,这个大小进行分片。

2

2、nio的文件分片操作

			
	        fin = new FileInputStream(localFilePath + filename);
    	    fcin = fin.getChannel();
        	long size = fcin.size();
			//nio的文件大小最好不要使用file.length()
	
			//size是文件的大小,计算得到要切分为几片,为保证最后一片要大于5M的要求,将剩余的大小加到最后一片上去
            long partNum = size / partFileSize;
            long remain = size % partFileSize;
            long splitSize = partFileSize;
            long finallySize = partFileSize + remain;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

使用 buffer.flip();记性切片。最后一片带着remain

            //ByteBuffer buffer = ByteBuffer.allocate((int) partFileSize); //15M一片
            //int count = 1;
            //while (count <= partNum) {
            //    buffer.clear();
            //    if (count == partNum) {
            //        buffer = ByteBuffer.allocate((int) finallySize);
            //    }
            //    int flag = fcin.read(buffer);
            //    if (flag == -1) {
            //        break;
            //    }
            //    buffer.flip();
            //    fout = new FileOutputStream(localFilePath + count + filename);
            //    fcout = fout.getChannel();
            //    fcout.write(buffer);
            //    log.info("文件分片生成 共{}个 第{}个 文件名{}", partNum, count, localFilePath + count + filename);
            //    count++;
            //}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

后来我觉得 buffer.filp可能有问题,导致我分片的上传成功率非常低,换用transferTo
从开始位置开始切片,每次切splitSize的大小,最后一片切splitSize+remain

			long startPoint = 0;
            for (int i = 1; i <= partNum; i++) {
                fout = new FileOutputStream(localFilePath + i + filename);
                fcout = fout.getChannel();
                if (i == partNum) {
                    splitSize = finallySize;
                }
                fcin.transferTo(startPoint, splitSize, fcout);
                startPoint += splitSize;
                fcout.close();
                fout.close();
            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3

3、还有一个注意的点,就是文件的上传,开放平台写的是直接上传一个video的数组,其实不是数组,就是一个完整的文件outputStream.toByteArray。
先读到inputStream中,再到outputStream.toByteArray,输入输出使用不同的流,不然io就会觉得不安全,可能会给报错

                log.info("开始上传第{}个文件 路径{}", i, localFilePath + i + filename);
                inputStream = new FileInputStream(new File(localFilePath + i + filename));//与根据File类对象的所代表的实际文件建立链接创建fileInputStream对象
                outStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = inputStream.read(buffer)) != -1) {
                    outStream.write(buffer, 0, len);
                }
                String uploadRst = douYinHttpService.douYinFilePost(uploadUrl + i, i + filename, outStream.toByteArray());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

请求头

HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.valueOf(MediaType.MULTIPART_FORM_DATA_VALUE));
        ContentDisposition co = ContentDisposition.builder("form-data").name(VIDEO).filename(filename).build();
        headers.setContentDisposition(co);

        HttpHeaders videoHeader = new HttpHeaders();
        videoHeader.setContentDispositionFormData(VIDEO, filename);
        ByteArrayResource bar = new ByteArrayResource(bytes);
        HttpEntity<ByteArrayResource> videoPart = new HttpEntity<>(bar, videoHeader);
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        body.add(VIDEO, videoPart);

        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity(body, headers);
        log.info("douYinFilePost 抖音File POST请求 url {}  参数 {}", url, videoPart);
        String content = null;
        try {
            content = restTemplate.postForObject(new URI(url), requestEntity, String.class);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

根据postman发送请求就能追到请求头是什么。

其他问题

文件的分片上传其实是上传的一个视频文件,比如用手机录制两个视频,5M以上(分片的要求文件要在5M以上),第一次 part-num是1,将视频上传了,第二次part-num是2上传第二个视频文件,最后complete完成上传,这种情况他其实只识别到第一个视频文件,账号发布视频以后发现视频的长度还是第一个的长度。

为什么会造成这种情况呢,因为第一片的上传完成后有视频的长度信息和结束标记位,后面的不管你上传再多的分片,他还是只能识别到第一个,以后的就不管了。

使用文件切割后,观察生成的分片之后的文件就会发现,其实第一个分片,windows在预览的时候能看出来是个视频文件,打开播放的时候能播放,到一个时间点以后就不能播放了,视频的长度也是总视频长度;而其他分片的视频文件,发现根本不能预览,双击打开之后也提示文件损坏。
如图:第一个是切片之前,紧挨着的是切之后第一个能预览能播放。其他啥也不行。
在这里插入图片描述

最终效果

换用1000100010这种切片方案,加nio的channel的transferTo方法,使用几个视频做切片测试,测试6个文件,其中5个能一次性就分片上传成功。最后一个分片之后多次上传都不成功。换1000100015这样进行切片,每片15M,一次上传成功。

遗留问题

后续引入重试机制,将发布不成功的视频按其他的分片大小进行切分,再次上传,写个次数限制,重试5次,多了实在还不行,上传失败的就给用户提示吧,我也没招了。

联系

尝试了4天,最终按这种方案解决了上传失败率太高的问题,基本能达到上传一个成功一个,不过也不能避免上传失败的情况,大神有什么指教可以公众号联系我。
后续想把网站完善下,弄上回复框,算是定一个小目标

个人网站
http://www.51pro.top
网站
公众号 含联系方式
公众号

20210929后续来了

问题: 交付给业务使用后频繁出现分片上传不合法的对象id

1、0927考虑业务给发的视频都是高清的视频,一秒10M,码率高,思路一寻找视频是不是不标准的?
2、0928出现自己的视频也传不上去了,和高清没什么关系,非高清的视频只要切片都是不合法的对象id
3、0928出现一次上传成功,0929出现一次上传成功。其他出现不合法的对象ID,怀疑抖音接口有问题,毕竟自己的代码能上传成功。
4、0928、0929提交工单给提示uploadid需要自己encode一次。毕竟有上传成功的,怀疑抖音平台问题。

尝试解决

1、

0927追视频问题,vlc播放器加mediaInfo软件看视频区别,达到了29M每秒。普通的视频几十KB每秒
在这里插入图片描述
0927追一天没发现视频有什么问题。晚上发非高清的视频,发现切片也会失败

2、

0928所有视频只要出现分片上传都会失败,一整天上传尝试一百次其中一次出现成功,妈的这失败高达99%,
但是其中还有一次成功的啊,不怀疑自己的代码有问题。0929上午出现一次成功,更不怀疑自己代码有问题了。

3、

0929工单信息
在这里插入图片描述

2021年09月27日 11:24
不合法的对象id 到底是什么id ???按10M大小进行切片 分片上传返回不合法的对象id uploadID 到底要不要自己encode? 能不能电话联系一下 XXXXXXXXXXXXXXXXXX
已关闭
查看
2021年09月27日 19:55
「工单回复」:
您好,uploadID encode一次即可
2021年09月28日 17:13
回复给「工单」:
logid 20210928170850010212070082390889A4 已经encode一次了 ,还是不行 为什么有的视频可以,高清的视频不可以呢 视频地址还是原来那个
图片链接:XXXXXXXXXX
2021年09月28日 17:22
回复给「工单」:
自己encode以后 能成功上传的视频都不能上传了 不加还能有成功的
2021年09月28日 17:23
回复给「工单」:
高清的视频 分片都不成功 不清晰的那种 分片都可以上传成功; 只要加上encode 全失败
2021年09月28日 17:23
回复给「工单」:
能不能技术人给打个电话
2021年09月28日 17:53
「工单回复」:
对于日志20210928170850010212070082390889A4,我们检查后发现,传入的upload_id被encode了两次,实际上只需encode一次即可
2021年09月28日 17:54
「工单回复」:
不主动encode 可以成功,就不要encode了,你使用的工具内部可能会自动帮你encode
2021年09月28日 17:54
「工单回复」:
高清视频上传不成功,请提供相应的logid
2021年09月28日 18:11
回复给「工单」:
20210928170850010212070082390889A4
2021年09月28日 18:14
「工单回复」:
你好20210928170850010212070082390889A4 这个是之前itemid encode两次导致出错的日志
2021年09月28日 18:27
回复给「工单」:
202109281824330101980671340F00654D
2021年09月28日 18:28
回复给「工单」:
这个logid呢 其实是直接拿的uploadid的返回值 看看这个是怎么回事
2021年09月28日 18:31
回复给「工单」:
logid:202109281824330101980671340F00654D logid:20210928182358010198066232370058B1 20210928182324010212026091290049B2 看看这三个的分片上传都失败了 是为什么
图片链接:XXXXXXXXXXXXXXXXXXXXX
2021年09月28日 18:35
回复给「工单」:
高清视频上传不成功logid : 2021092818323801020204808429013C35 202109281833200102040482303D0105B7 2021092818340301021216203212013FDE 202109281834460102121860185201BE88
2021年09月28日 18:51
「工单回复」:
你好,辛苦再提供下上传视频部分的代码,以及upload_id
2021年09月28日 19:07
回复给「工单」:
视频地址 https://test-pinpaiyunyingbop-lingxi.oss-cn-beijing.aliyuncs.com//spreaduatfile/images/20210928/15757420c3e9e6ae8350ce2a863796a6/56ed5d87-c606-eb10-6a0e-38ebe04a6789.mp4
2021年09月28日 19:07
回复给「工单」:
upload_id=@9VwC1+GVDss1K23yZdEvTM6v0zC8Nf2FPMAn/A3yKFQROPf10iLiLFIhgQby5DoKZq6NSUBH1aHz+2F7oSfViQ==
2021年09月28日 19:07
回复给「工单」:
“logid”:“202109281903490102120381024503F418”
2021年09月28日 19:08
回复给「工单」:
代码截图,代码是可以上传视频的 只是能偶然一次上传成功,99的概率上传失败
图片链接:XXXXXXXXXXX
2021年09月28日 19:09
回复给「工单」:
高清视频地址 https://test-pinpaiyunyingbop-lingxi.oss-cn-beijing.aliyuncs.com//spreaduatfile/images/20210928/9c364275ba85fa114633fdac022a7a07/f7ae3ce9-e446-57f3-2252-30a93c9be292.mp4
2021年09月28日 19:11
回复给「工单」:
高清视频的upload_id=@9VwC1+GVDss1K23yZdEvTM6o0TPsPfnQO5NxoAvwJ1QROPn91Hi3LAwhgQby5DoKH3Rt+cf0jIRPLoxgvuchKA==&part_number=1 “logid”:“2021092819101101021205920833049DCC”
2021年09月28日 19:14
回复给「工单」:
代码并不是说全部都不能上传成功 ,中间出现少次可以成功的,不过是99概率的失败
图片链接:XXXXXXXXXXXXXX
2021年09月28日 21:05
「工单回复」:
可以再提供下upload_id 部分的代码吗?包括给upload_id 做encode的操作的代码
2021年09月29日 08:01
回复给「工单」:
/video/part/init/ 直接用的这个接口的返回值data里面的upload_id
2021年09月29日 08:22
回复给「工单」:
这个uploadid是可以用的 如果encode之后就会变成两次encode,之前一直用的没问题,自从9.26以后,每次上传都失败,都是不合法的对象id,26-29号之间只有一次分片上传成功,9点半左右贴uppload_id的代码
2021年09月29日 09:39
回复给「工单」:
这个代码用了一个月了,一直是没有问题的,27号左右出现分片上传都失败,昨天就成功一次
图片链接:XXXXXXXXXXXX
2021年09月29日 09:40
回复给「工单」:
接口返回的uoloadid,直接使用,可以上传
2021年09月29日 09:53
回复给「工单」:
前天 昨天的所有分片上传 成功过一次 其他全部失败 都是这个不合法的对象id
2021年09月29日 10:25
回复给「工单」:
追问一句 开放平台在一周以内有更新吗 8月份到9月中旬都是能正常使用的,也申请了生产环境的应用,最近这三天突然发现分片上传出问题
2021年09月29日 15:44
「工单回复」:
最近几次的请求的upload_id中存在 出现问题,建议使用原生的方法,自己主动encode upload_id,再次尝试请求
2021年09月29日 17:13
回复给「工单」:
获取upload_id的返回值 就是分片上传使用的id,在一周之前是没有出现这个不合法对象id的问题,最近3天出现的这个问题。在没有encode的情况下,今天还成功过一次; urlencoder.encode(upload_id)之后,从来没有成功过。而且工单中回复的说encode了两遍;为什么不encode能成功过1次,而encode之后却完全没有成功过,工单还回复加了两次?
2021年09月29日 22:18
回复给「工单」:
解决了 restTemplete的post方法源码中有对url进行转码的操作,操作了最后两位的等号 关闭工单吧

0929问题解决

1、工单一直提示是uploadid有问题,需要encode一次,尝试使用postman发送,一个月之前使用postman分片上传的时候,uploadid不需要encode就能上传成功,这次尝试高概率返回不合法的对象id,使用工具encode之后发现 居然上传成功了。
这个现象是完全超过认知,为什么以前不需要做的,都能上传成功,现在的都不行了?反而加密后竟然可以了。

2、继续代码中encode,尝试多次后,postman发动的encode uploadid之后的上传都能成功,可是代码中出现的encode之后的全部失败。
为什么
3、怀疑代码发送后出现其他操作给我转码了。查询发送请求的代码。跟请求,打断点。
发请求使用的是restTemplete,追进去看看
这里调用的是post直接传的url,而正好,抖音开放平台中的uploadid是url中的参数,往里追
在这里插入图片描述
最后发现这里对url进行了转码操作
在这里插入图片描述
url中含有uploadid 最后的两个等号转换了%3D,其他的东西没有转换,这导致我的参数就发生了变化。
4、我怎么去掉转码这一步操作呢?

        //UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        //String content = restTemplate.postForObject(builder.build(false).toUri(), requestEntity, String.class);
  • 1
  • 2

第一种思路,这样尝试几次还是不行。
5、妈的 不用restTemplate了,自己写httpClient

bytes是read的子节流的数组
 //public String originPost(String url, String filePath, String filename, byte[] bytes) throws Exception {
    //    String result = "";
    //    CloseableHttpClient httpClient = HttpClients.createDefault();
    //    CloseableHttpResponse response = null;
    //    try {
    //        HttpPost post = new HttpPost(url);
    //        //设置请求体
    //        //List<NameValuePair> content = new ArrayList<NameValuePair>();
    //        //content.add(new BasicNameValuePair("video", bytes.toString()));
    //        //UrlEncodedFormEntity e = new UrlEncodedFormEntity(content, "UTF-8");
    //        //post.setEntity(e);
    //       我这里尝试了好几种设置请求头的方案,用了好几个entry,追了几个Entity的实现类,尝试好几次后均失败---------------------------------
    //        MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
    //        entityBuilder.addPart("video", new FileBody(new File("XXXXXXXXXXXXXXXXXXXXXXXXXXXX")));
    //        entityBuilder.setCharset(Charset.forName("UTF-8")).addBinaryBody("video", new FileInputStream(filePath), ContentType.MULTIPART_FORM_DATA, filename);
    //
    //        post.setEntity(entityBuilder.build());
    //        //设置请求头
    //        post.addHeader("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE);
    //        post.addHeader("Content-Disposition", "name=\"video\"; filename=" + filename);
    //
    //        //发送请求
    //        response = httpClient.execute(post);
    //        if (response != null && response.getStatusLine().getStatusCode() == 200) {
    //            org.apache.http.HttpEntity entity = response.getEntity();
    //            result = EntityUtils.toString(entity);
    //        }
    //    } catch (Exception e) {
    //        log.info("originPost", e.getMessage());
    //    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

这几个写法一直返回的是系统正忙,请稍后再试,总觉得是自己的请求不对,请求头的参数没设置好。

心情实在是糟糕透了,很长时间没有弄好这个请求头,废了很长时间,都没整好,tcp http没学好,回去再自学去吧,不会写了。放弃。
如果有谁能把这个方法补充上也能发送成功。
后续哪位高手看见补上给留言,感激不尽
在这里插入图片描述

最终方案

还是使用restTemplate,尝试一下这个方案,找post的重载的方法,看看有没有不转码的方法。看到一个这个,传URI,不会转码

            content = restTemplate.postForObject(new URI(url), requestEntity, String.class);

  • 1
  • 2

皆大欢喜!!! 成功
后续创建视频也是使用这个encode之后的uploadid,还是用URI,不让他转码,自己手动转。

优化

增加失败重试机制,多试几次,单次切片保持一个大小,多次切片的大小不同,第一次每片10M,第二次每片12M,依次涨点,最后还要保证最后一片的大小大于5M。
最后再来一次托底的操作,最后一次尝试如果小于100M,使用一次单片上传。大于100M最后一次尝试切片。

    private static final Map<Integer, Long> redoCountPartSizeMap = new HashMap<>();

    static {
        redoCountPartSizeMap.put(1, 1000 * 1000 * 10L);
        redoCountPartSizeMap.put(2, 1000 * 1000 * 12L);
        redoCountPartSizeMap.put(3, 1000 * 1000 * 13L);
        redoCountPartSizeMap.put(4, 1000 * 1000 * 14L);
        redoCountPartSizeMap.put(5, 1000 * 1000 * 15L);
        redoCountPartSizeMap.put(6, 1000 * 1000 * 18L);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

总结

网络的文件下载到本地
本地文件切片
获取uploadid
encode操作
分片上传,注意不能让http给转码
完成上传
创建视频。

2021-11-20
后续又来了
几天前发现有几个视频一直在请求审核状态,发现抖音平台视频状态细化了,从11.15以后状态码和之前的对不起来了,导致一直在请求审核状态。
看开放平台video_status i32 表示视频状态。1:细化为5、6、7三种状态;2:不适宜公开;4:审核中;5:公开视频;6:好友可见;7:私密视频

怎么看都怀疑是之前提交的一个工单 审核结束但返回状态是审核中的问题
再改改代码走起

内存溢出了!!!!

2022-02-10
java.lang.OutOfMemoryError: Java heap space
问题代码和报错不贴了,涉密。。。。。

问题现象

过完年刚回来,公司业务推广,不知道哪个楼盘活动推广加城市宣传片,点击发送以后,后台收到短信报异常,紧接着SLB异常报警,APM监控报警!!!!!!!!!
事态紧急,手机上收到各种报警,4台服务器有2台AMP监控超时,网关下掉服务器通知,幸亏是晚上10点多没多少人用,抓紧devops流水线重启,网关重新上线。

故障复盘

业务上传视频文件发送到多个抖音账号,抖音开放平台限制单个文件大小4G,业务人员不知道从哪里弄了一个3.5G大小的视频文件,正好他手底下有10个账号权限,点了全选,一下发到了10个账号上去,后面4台服务器收到队列消息,直接就开始下载文件处理,其中两台正好文件下载了3遍,在做文件切割的时候出现内存溢出。

业务没有强制需求做账号多选,产品出了多选发布的功能,我后台开发没对账号上锁做单步,三方都有问题,一起把问题背了下来。

代码修改

1、首先考虑到是OOM异常,日志里的异常信息打到了lamdba表达式里面,没有具体的行数。既然是内存小,先考虑heap溢出用Xmx扩大一下,发现原来设置过这个参数,不让我们自己调整,换G1垃圾回收器,-XX:+UseG1GC。
大对象直接在堆里面了,那我不区分新生代和老年代不行吗,G1里面新生代和老年代他自己转,用G1总比用PS+PO强一点吧。
2、正好业务选了10个账号,要是再来两个,后台4个服务器都得被干死。这么大的文件,多来几个谁也受不了,加分布式锁。
消息来之后先加锁。获取不到锁的去延时队列,延迟个1min再消费;redis先锁视频文件id,再锁账号id,虽然大视频发的慢点,保证服务器死不了就行。
3、拆开lamdba,防止自己生成那么多的class不好追踪。增加各种日志打印。第二天紧急发版。

后记

幸亏是年后出现的问题,要是年前,不堪设想啊。。。
产品、业务、开发、测试 这些人关系够铁,有问题大家一起扛,欣慰。
以后要特别注意这种大文件的处理,还同时处理多个,想想都后怕,万一所有服务器都干趴了,就不是这个结果了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/267843
推荐阅读
相关标签
  

闽ICP备14008679号