阿里云视频点播文件上传-iOS

阿里云视频点播文件上传-iOS

写在前面:

阿里云-上传SDK文档
阿里云-Api常见错误码表

使用前需先开通视频点播权限,见文档,否则Api访问无效,上传不上去的。

如果只是单纯的上传文件,而不是点播文件,使用简单上传就可以了,视频点播上传是针对点播服务的。刚开始不太清楚,搞了半天视频点播上传,发现Api访问无权限,后来才看到需要开通服务。

一、上传方式

1、上传地址加凭证

2、STS方式上传

方式一 上传地址加凭证上传

这是阿里官方推荐使用的上传方式

与STS方式流程基本一致,只有4点不同的地方

1、请求AppServer

请求AppServer的时候需要让server返回uploadAddress(上传地址)和uploadAuth(上传凭证)

2、在start的回调中设置上传地址和上传凭证

	OnUploadStartedListener UploadStartedCallbackFunc = ^(UploadFileInfo* fileInfo) {
		NSLog(@"upload upload started callback.");
		// 设置上传地址 和 上传凭证
		[weakSelf.uploader setUploadAuthAndAddress:fileInfo uploadAuth:`upload auth` uploadAddress:`upload address`];
	};	

3、uploadAuth过期重新设置

	OnUploadTokenExpiredListener TokenExpiredCallbackFunc = ^{
		NSLog(@"upload token expired callback.");
		// token过期,设置新的上传凭证,继续上传
		[weakSelf.uploader resumeWithAuth:`new upload auth`];
	};

4、上传图片和上传视频

STS上传视频和地址,请求AppServer返回都是key,secreatKey,token和过期时间,就可以上传视频和图片资源。

上传地址+凭证的上传方式注意:

  • 客户端上传视频:需要请求向AppServer发送请求,AppServer通过OpenApi向阿里云点播服务发送CreateUploadVideo请求。请求成功将返回上传地址,上传凭证以及VideoId,AppServer需要将结果返回给客户端。

  • 客户端上传图片:需要请求向AppServer发送请求,AppServer通过OpenApi向阿里云点播服务发送CreateUploadImage请求。请求成功将返回上传地址,上传凭证以及ImageURL,AppServer需要将结果返回给客户端。

方式二 STS方式上传

不推荐使用,可能会被废弃

1、请求STS

向自己的AppServer请求相关的AccessKeyId、AccessKeySecret、SecurityToken等信息。

2、初始化上传对象

初始化VODUploadClient对象,并调用以下方法设置STS授权信息

- (BOOL)        init:(NSString *)accessKeyId
     accessKeySecret:(NSString *)accessKeySecret
         secretToken:(NSString *)secretToken
          expireTime:(NSString *)expireTime
            listener:(VODUploadListener *) listener

STS方式调用init: accessKeySecret: secretToken: expireTime: listener:方法初始化,初始化参数即是第一步请求获取的临时STS凭证。

想不明白起名为什么要起init…,最新版的SDK里Api有所改动,这个方法改成了setAccessKeyId…。

3、回调设置

在初始化uploader对象的时候,顺便设置事件回调。

4、添加上传文件进入上传列表,支持视频文件和图片文件的上传

- (BOOL)addFile:(NSString *)filePath
        vodInfo:(VodInfo *)vodInfo;

5、启动上传

- (BOOL)start;

6、回调处理

略…

二、注意事项

  • 1.5.3版本的readme里面,推荐使用上传凭证的方式上传

  • STS上传方式可能会被废弃

  • 使用VODUpload.framework的话需要后台开通视频点播服务权限,否则接口访问受限

三、错误和异常处理

1、Undefined symbols for architecture x86_64

未添加依赖库,看Demo里有添加libresolv.tbd,libresolv.9.tbd、SystermConfigration.framework。

2、[VODUploadClient initMultiUpload]_block_invoke

初始化问题,未调用init方法设置STS授权信息。

3、failed code = (null), error message = (null)

报错如下

2019-10-21 11:09:02.791887+0800 UploadDemo[5606:1449184] upload started . 2019-10-21 11:09:02.802116+0800 UploadDemo[5606:1449184] failed code = (null), error message = (null) 2019-10-21 11:09:02.802214+0800 UploadDemo[5606:1449184] state 5

不知道怎么处理,更新最新的SDK吧,旧的SDK是有这个问题。

4、Undefined symbols:_CMTimeGetSeconds

报错如下

Undefined symbols for architecture arm64: "_CMTimeRangeGetEnd", referenced from: +[AVCVAssetInfo AVCVVideoDuration:] in VODUpload(AVCVAssetInfo.o) +[AVCVAssetInfo AVCVAudioDuration:] in VODUpload(AVCVAssetInfo.o) "_CMTimeGetSeconds", referenced from: +[AVCVAssetInfo AVCVDuration:] in VODUpload(AVCVAssetInfo.o) +[AVCVAssetInfo AVCVVideoDuration:] in VODUpload(AVCVAssetInfo.o) +[AVCVAssetInfo AVCVAudioDuration:] in VODUpload(AVCVAssetInfo.o) "_AVMediaTypeAudio", referenced from: +[AVCVAssetInfo AVCVAudioDuration:] in VODUpload(AVCVAssetInfo.o) "_AVMediaTypeVideo", referenced from: +[AVCVAssetInfo AVCVNaturalSize:] in VODUpload(AVCVAssetInfo.o) +[AVCVAssetInfo AVCVFrameRate:] in VODUpload(AVCVAssetInfo.o) +[AVCVAssetInfo AVCVBitrate:] in VODUpload(AVCVAssetInfo.o) +[AVCVAssetInfo AVCVVideoDuration:] in VODUpload(AVCVAssetInfo.o) "_OBJC_CLASS_$_AVURLAsset", referenced from: objc-class-ref in VODUpload(VODUploadClient.o) ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation)
官网上说要添加-Objc,force load,统统删掉,编译,OK了。

但是一初始化上传对象,编译,又报错了,什么情况呢?后来联系了阿里的技术服务和技术的童鞋。

原来是缺少了依赖库,也不用设置force load,-Objc什么的,需要添加的依赖库有:

libresolve.tbd
libresolve.9.tbd
SystermCongiguration.framework
MobileCoreService.framework
CoreMedia.ramework
AVFoundation.framework

这些官方文档上可是没有说的,就是对着Demo添加也添加不全的。

5、Forbidden.RAM

上传报错

failed code = Forbidden.RAM, error message = You are not authorized to operate this resource, or this API does not support RAM.

报这个错误,是没有权限使用上传Api,我是因为后台没有开通点播服务导致的。

四、实现代码

这个代码不是很合理,看看就行了。

STS上传方式源码

STS已经不被推荐使用了,这里直接跳过吧。

#import "VHVideoUploder.h"
#import <VODUpload/VODUploadClient.h>
#import "VHVideoUploadModel.h"
#import "VHWebService.h"

@interface VHVideoUploder ()

@property (nonatomic, strong) VODUploadClient *uploader;
@property (nonatomic, strong) VODUploadListener *listener;

@property (nonatomic, copy) NSString *userAccessToken;

@end

@implementation VHVideoUploder

/**
 获取SDK版本号
 */
+ (NSString *)getSDKVersion {
    return SDK_Version;
}

#pragma mark - init

- (VODUploadClient *)uploader {
    if (!_uploader) {
        _uploader = [[VODUploadClient alloc] init];
        
        __weak typeof(self)weakSelf = self;
        
        OnUploadFinishedListener finishedCallbackFunc = ^(UploadFileInfo* fileInfo, VodUploadResult* result) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"%@", [NSString stringWithFormat:@"upload success! %@", fileInfo.filePath]);
                
            });
        };
        
        OnUploadFailedListener testFailedCallbackFunc = ^(UploadFileInfo* fileInfo, NSString *code, NSString* message){
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"failed code = %@, error message = %@", code, message);
                NSLog(@"state %ld",(long)fileInfo.state);
            });
        };
        
        OnUploadProgressListener testProgressCallbackFunc = ^(UploadFileInfo* fileInfo, long uploadedSize, long totalSize) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"progress uploadedSize : %li, totalSize : %li", uploadedSize, totalSize);
                
            });
        };
        
        OnUploadTokenExpiredListener testTokenExpiredCallbackFunc = ^{
            NSLog(@"token expired.");
            // update sts token and call resumeWithToken
            
            // OSS token过期,设置新的STS,继续上传
            //[weakSelf.uploader resumeWithToken:`STS Key Id` accessKeySecret:`STS Key Secret` secretToken:`STS Secret Token` expireTime:`STS Expire Time`];
            
            dispatch_async(dispatch_get_main_queue(), ^{
                
                [VHVideoUploder requestUploadAuthKeyWithToekn:self.userAccessToken success:^(id  _Nullable responseObject) {
                    
                    [weakSelf.uploader resumeWithToken:responseObject[@"stsArr"][@"AccessKeyId"] accessKeySecret:responseObject[@"stsArr"][@"AccessKeySecret"]  secretToken:responseObject[@"stsArr"][@"SecurityToken"] expireTime:responseObject[@"stsArr"][@"Expiration"] ];
                    
                } failure:^(NSError *error) {
                    
                }];
            });
        };
        
        OnUploadRertyListener testRetryCallbackFunc = ^{
            NSLog(@"manager: retry begin.");
        };
        
        OnUploadRertyResumeListener testRetryResumeCallbackFunc = ^{
            NSLog(@"manager: retry begin.");
        };
        
        OnUploadStartedListener testUploadStartedCallbackFunc = ^(UploadFileInfo* fileInfo) {
            NSLog(@"upload started .");
            
        };
        
        _listener = [[VODUploadListener alloc] init];
        _listener.finish = finishedCallbackFunc;
        _listener.failure = testFailedCallbackFunc;
        _listener.progress = testProgressCallbackFunc;
        _listener.expire = testTokenExpiredCallbackFunc;
        _listener.retry = testRetryCallbackFunc;
        _listener.retryResume = testRetryResumeCallbackFunc;
        _listener.started = testUploadStartedCallbackFunc;
    }
    return _uploader;
}


#pragma mark - public

/**
 添加文件
 
 @param videoPath 视频文件路径
 @param svideoInfo 短视频上传信息
 */
- (BOOL)addFileWithVideoPath:(NSString *)videoPath
                  svideoInfo:(VHVideoUploadModel *)svideoInfo
{
    VodInfo *info = [[VodInfo alloc] init];
    info.title = svideoInfo.name;
    info.desc = svideoInfo.desc;
    return [self.uploader addFile:videoPath vodInfo:info];
}

/**
 开始上传
 
 @param accessToken VH AccessToken
 */
- (void)startWithAccessToken:(NSString *)accessToken
{
    self.userAccessToken = accessToken;
    
    [VHVideoUploder requestUploadAuthKeyWithToekn:accessToken success:^(id  _Nullable responseObject) {

        [self.uploader setKeyId:responseObject[@"stsArr"][@"AccessKeyId"] accessKeySecret:responseObject[@"stsArr"][@"AccessKeySecret"] secretToken:responseObject[@"stsArr"][@"SecurityToken"] expireTime:responseObject[@"stsArr"][@"Expiration"] listener:self.listener];

        [self.uploader start];

    } failure:^(NSError *error) {

    }];
}


/**
 暂停上传
 */
- (void)pause
{
    [self.uploader pause];
}

/**
 恢复上传
 */
- (void)resume
{
    [self.uploader resume];
}

#pragma mark - private

+ (void)requestUploadAuthKeyWithToekn:(NSString *)toekn
                              success:(nullable void (^)(id _Nullable responseObject))success
                              failure:(nullable void (^)(NSError *error))failure
{
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    dict[@"access_token"] = toekn;
    [VHWebService requestWithApi:UploadAuthKey param:dict onRequestFinished:^(id data) {
        NSLog(@"request authKey response : %@",data);
        if (success) {
            success(data);
        }
    } onRequestFailed:^(NSError *error) {
        NSLog(@"request authKey error : %@",error);
        if (failure) {
            failure(error);
        }
    }];
}

@end

这个代码测试了一下没跑通,OSS SDK报错信息如下:

failed code = Forbidden.RAM, error message = You are not authorized to operate this resource, or this API does not support RAM.

然后阿里技术的童鞋说推荐使用上传地址+凭证的方式上传。查了Api错误表,搜索‘API does not support RAM’关键字,会有解释,这个Api不能用了:

’未授权,或此 API 不支持访问控制。‘

安卓端也是报这个问题,‘临时用户访问无权限,该临时用户角色扮演指定授权策略,该授权策略无权限’。

上传地址+凭证方式源码

VHVideoUploader.m

//
//  VHVideoUploader.m
//  VHUploadFramework
//
//  Created by vhall on 2019/10/15.
//  Copyright © 2019 vhall. All rights reserved.
//

#import "VHVideoUploader.h"
#import <VODUpload/VODUploadClient.h>
#import "VHVideoUploadModel.h"
#import "VHWebService.h"

@interface VHVideoUploader ()

@property (nonatomic, strong) VODUploadClient *uploader;
@property (nonatomic, strong) VODUploadListener *listener;

@property (nonatomic, copy) NSString *userAccessToken;

@property (nonatomic, copy) NSString *uploadAuth;//上传凭证
@property (nonatomic, copy) NSString *uploadAddress;//上传地址

@end

@implementation VHVideoUploader

/**
 获取SDK版本号
 */
+ (NSString *)getSDKVersion {
    return SDK_Version;
}

#pragma mark - init

- (VODUploadClient *)uploader {
    if (!_uploader) {
        _uploader = [[VODUploadClient alloc] init];
        
        __weak typeof(self)weakSelf = self;
        
        OnUploadFinishedListener finishedCallbackFunc = ^(UploadFileInfo* fileInfo, VodUploadResult* result) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"%@", [NSString stringWithFormat:@"upload success! %@", fileInfo.filePath]);
                [weakSelf finished:fileInfo result:result];
            });
        };
        
        OnUploadFailedListener testFailedCallbackFunc = ^(UploadFileInfo* fileInfo, NSString *code, NSString* message){
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"failed code = %@, error message = %@", code, message);
                NSLog(@"state %ld",(long)fileInfo.state);
                [weakSelf failed:fileInfo code:code message:message];
            });
        };
        
        OnUploadProgressListener testProgressCallbackFunc = ^(UploadFileInfo* fileInfo, long uploadedSize, long totalSize) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"progress uploadedSize : %li, totalSize : %li", uploadedSize, totalSize);
                [weakSelf progress:fileInfo uploadSize:uploadedSize totalSize:totalSize];
            });
        };
        
        OnUploadTokenExpiredListener testTokenExpiredCallbackFunc = ^{
            NSLog(@"token expired.");
            
            // upload auth过期,设置新的upload auth,继续上传
            // [weakSelf.uploader resumeWithAuth:`new upload auth`];
            
            dispatch_async(dispatch_get_main_queue(), ^{
                
                [VHVideoUploader requestUploadAuthKeyWithToekn:self.userAccessToken success:^(id  _Nullable responseObject) {
                    
//                    weakSelf.uploadAuth =
//                    weakSelf.uploadAddress =
                    
                    [weakSelf.uploader resumeWithAuth:weakSelf.uploadAuth];
                    
                } failure:^(NSError *error) {
                    
                }];
            });
        };
        
        OnUploadRertyListener testRetryCallbackFunc = ^{
            NSLog(@"manager: retry begin.");
        };
        
        OnUploadRertyResumeListener testRetryResumeCallbackFunc = ^{
            NSLog(@"manager: retry begin.");
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf onResume];
            });
        };
        
        OnUploadStartedListener testUploadStartedCallbackFunc = ^(UploadFileInfo* fileInfo) {
            NSLog(@"upload started .");
            
            // 设置上传地址 和 上传凭证
            //[weakSelf.uploader setUploadAuthAndAddress:fileInfo uploadAuth:`upload auth` uploadAddress:`upload address`];

            [weakSelf.uploader setUploadAuthAndAddress:fileInfo uploadAuth:weakSelf.uploadAuth uploadAddress:weakSelf.uploadAddress];
            
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf start:fileInfo];
            });
        };
        
        
        _listener = [[VODUploadListener alloc] init];
        _listener.finish = finishedCallbackFunc;
        _listener.failure = testFailedCallbackFunc;
        _listener.progress = testProgressCallbackFunc;
        _listener.expire = testTokenExpiredCallbackFunc;
        _listener.retry = testRetryCallbackFunc;
        _listener.retryResume = testRetryResumeCallbackFunc;
        _listener.started = testUploadStartedCallbackFunc;
    }
    return _uploader;
}


#pragma mark - public

/**
 添加文件
 
 @param videoPath 视频文件路径
 @param model 短视频上传信息
 */
- (BOOL)addFileWithVideoPath:(NSString *)videoPath
                  svideoInfo:(VHVideoUploadModel *)model
{
    return [self.uploader addFile:videoPath vodInfo:[self unPackVodInfo:model]];
}

/**
 开始上传
 
 @param accessToken VH AccessToken
 */
- (void)startWithAccessToken:(NSString *)accessToken
{
    self.userAccessToken = accessToken;
    
    [VHVideoUploader requestUploadAuthKeyWithToekn:accessToken success:^(id  _Nullable responseObject) {

//        self.uploadAuth =
//        self.uploadAddress =

        [self.uploader start];

    } failure:^(NSError *error) {

    }];
}


/**
 暂停上传
 */
- (void)pause
{
    [self.uploader pause];
}

/**
 恢复上传
 */
- (void)resume
{
    [self.uploader resume];
}

#pragma mark - private

+ (void)requestUploadAuthKeyWithToekn:(NSString *)toekn
                              success:(nullable void (^)(id _Nullable responseObject))success
                              failure:(nullable void (^)(NSError *error))failure
{
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    dict[@"access_token"] = toekn;
    [VHWebService requestWithApi:UploadAuthKey param:dict onRequestFinished:^(id data) {
        NSLog(@"request authKey response : %@",data);
        if (success) {
            success(data);
        }
    } onRequestFailed:^(NSError *error) {
        NSLog(@"request authKey error : %@",error);
        if (failure) {
            failure(error);
        }
    }];
}

- (VHUploadFileInfo *)packUploadInfo:(UploadFileInfo *)fileInfo {
    VHUploadFileInfo *info = [[VHUploadFileInfo alloc] init];
    info.filePath = fileInfo.filePath;
    info.endpoint = fileInfo.endpoint;
    info.bucket = fileInfo.bucket;
    info.object = fileInfo.object;
    info.state = (VHVODUploadFileStatus)fileInfo.state;
    info.vodInfo = [self packUploadModel:fileInfo.vodInfo];
    return info;
}
- (VHVideoUploadModel *)packUploadModel:(VodInfo *)vodInfo {
    VHVideoUploadModel *info = [[VHVideoUploadModel alloc] init];
    info.title = vodInfo.title;
    info.tags = vodInfo.tags;
    info.desc = vodInfo.desc;
    info.cateId = vodInfo.cateId;
    info.coverUrl = vodInfo.coverUrl;
    info.userData = vodInfo.userData;
    return info;
}

- (VodInfo *)unPackVodInfo:(VHVideoUploadModel *)model {
    VodInfo *info = [[VodInfo alloc] init];
    info.title = model.title;
    info.tags = model.tags;
    info.desc = model.desc;
    info.cateId = model.cateId;
    info.coverUrl = model.coverUrl;
    info.userData = model.userData;
    return info;
}

- (VHVodUploadResult *)packUploadResult:(VodUploadResult *)result
{
    VHVodUploadResult *uploadResult = [[VHVodUploadResult alloc] init];
    uploadResult.videoId = result.videoId;
    uploadResult.imageUrl = result.imageUrl;
    uploadResult.bucket = result.bucket;
    uploadResult.endpoint = result.endpoint;
    return uploadResult;
}


#pragma mark - 回调
- (void)start:(UploadFileInfo *)fileInfo {
    if ([self.delegate respondsToSelector:@selector(uploader:didStarted:)]) {
        [self.delegate uploader:self didStarted:[self packUploadInfo:fileInfo]];
    }
}
- (void)failed:(UploadFileInfo *)fileInfo code:(NSString *)code message:(NSString *)message {
    if ([self.delegate respondsToSelector:@selector(failed:code:message:)]) {
        [self.delegate uploader:self didFailed:[self packUploadInfo:fileInfo] code:code message:message];
    }
}
- (void)finished:(UploadFileInfo *)fileInfo result:(VodUploadResult *)reult {
    if ([self.delegate respondsToSelector:@selector(uploader:didFinished:result:)]) {
        [self.delegate uploader:self didFinished:[self packUploadInfo:fileInfo] result:[self packUploadResult:reult]];
    }
}
- (void)onResume {
    if ([self.delegate respondsToSelector:@selector(uploaderDidResume:)]) {
        [self.delegate uploaderDidResume:self];
    }
}
- (void)progress:(UploadFileInfo *)fileInfo uploadSize:(long)uploadedSize totalSize:(long)totalSize  {
    
    
    if ([self.delegate respondsToSelector:@selector(uploader:progressFile:uploadSize:totalSize:)]) {
        [self.delegate uploader:self progressFile:[self packUploadInfo:fileInfo] uploadSize:uploadedSize totalSize:totalSize];
    }
}

@end

VHVideoUploadModel.h

//
//  VHVideoUploadModel.h
//  VHUploadFramework
//
//  Created by vhall on 2019/10/15.
//  Copyright © 2019 vhall. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

typedef NS_ENUM(NSInteger, VHVODUploadFileStatus) {
    VHVODUploadFileStatusReady,
    VHVODUploadFileStatusUploading,
    VHVODUploadFileStatusCanceled,
    VHVODUploadFileStatusPaused,
    VHVODUploadFileStatusSuccess,
    VHVODUploadFileStatusFailure
};



@interface VHVideoUploadModel : NSObject

/**
 标题
 */
@property (nonatomic, copy) NSString* title;

/**
 标签
 */
@property (nonatomic, copy) NSString* tags;

/**
 描述
 */
@property (nonatomic, copy) NSString* desc;

/**
 分类id
 */
@property (nonatomic, strong) NSNumber* cateId;

/**
 封面url
 */
@property (nonatomic, copy) NSString* coverUrl;

/**
 设置自定义数据
 */
@property (nonatomic, copy) NSString* userData;

@end


@interface VHUploadFileInfo : NSObject

@property (nonatomic, copy) NSString* filePath;
@property (nonatomic, copy) NSString* endpoint;
@property (nonatomic, copy) NSString* bucket;
@property (nonatomic, copy) NSString* object;
@property (nonatomic, strong) VHVideoUploadModel* vodInfo;
@property VHVODUploadFileStatus state;

@end


@interface VHVodUploadResult: NSObject
@property (nonatomic, copy) NSString* videoId;
@property (nonatomic, copy) NSString* imageUrl;
@property (nonatomic, copy) NSString* bucket;
@property (nonatomic, copy) NSString* endpoint;
@end

NS_ASSUME_NONNULL_END

这种每次回调的时候都需要将数据包装,在progress回调方法中一直创建对象的方法不是很妥当,还需要优化。

发布了131 篇原创文章 · 获赞 9 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/Morris_/article/details/102742148