前言

最近的工作是负责写埋点 SDK,学到了很多知识,不仅仅是 iOS 方面的,还包括 JavaScriptNodeJS大数据等方面的一些知识,有时间会把自己学到的知识全部记录下来,一则是加深印象,二是如果恰好遇到有需要的人,希望能提供一些思路。

正文

1.为什么要用表单格式上传 OSS

为什么要用表单格式上传数据到 OSS,大家知道,如果借用 AliyunOSSiOS 的话,会非常 easy 且轻松搞定 OSS 数据上传,那么为什么非要拼接成表单格式上传到 OSS 呢?

这就和具体需求有关了。

我们的需求是,为公司的几个 APP 做埋点 SDK,考虑到这几个 APP 都有可能使用 AliyunOSSiOS,那么为了避免冲突,兼容本身已集成 AliyunOSSiOS 的 APP,我们的埋点 SDK 就不能再集成 AliyunOSSiOS 了,于是就选择了表单格式上传。

2.核心代码及思路

2.1 先调用服务端接口获取签名

 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
[NNNetworking requestWithURL:@"获取签名的接口" params:@{@"key":@"value"} success:^(id  _Nullable responseObject) {
    if (responseObject) {
        NSDictionary *origDict = responseObject;
        NSString *statusCode = origDict[@"statusCode"];
        if ([statusCode isEqualToString:@"200"]) {
            NSDictionary *resultDict = origDict[@"result"];
            NSString *policy = resultDict[@"policy"];
            NSString *OSSAccessKeyId = resultDict[@"OSSAccessKeyId"];
            NSString *signature = resultDict[@"signature"];
            NSString *key = resultDict[@"dirPath"];
            
            NSMutableDictionary *parameterDict = [[NSMutableDictionary alloc]init];
            [parameterDict setValue:OSSAccessKeyId forKey:@"OSSAccessKeyId"];
            [parameterDict setValue:policy            forKey:@"policy"];
            [parameterDict setValue:signature         forKey:@"signature"];
            [parameterDict setValue:key forKey:@"key"];
            // 上传数据到 OSS
            [NNNetworking uploadOSSWithParameter:parameterDict uploadDataArray:@[] success:^(id  _Nonnull obj) {
                
            } fail:^(NSError * _Nonnull error) {
                
            }];
        }
    }
} failure:^(NSError * _Nullable error) {
    
}];

由于自己做的本身就是 SDK,所以强制不能使用第三方框架,比如常用的网络框架 AFNetworking 或 Alamofire 都不能使用。

2.2 接着拼接签名、策略、key 等字段,拼接成表单结构,然后上传至 OSS

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
+ (void)uploadOSSWithParameter:(NSDictionary *)parameterDict uploadDataArray:(NSArray *)dataArray success:(void (^)(id obj))success fail:(void (^)(NSError *error))fail {
    // 分界线的标识符
    NSString *TWITTERFON_FORM_BOUNDARY = @"IaX0GHJdM5gRIXY_S37dFzTjrYcygaI4lr8JjgHY_hUURrbjP8LbpzJ7KEQ2AbcY5yU3Jc";
    // 根据 url 初始化 request
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://桶名.oss-cn-beijing.aliyuncs.com"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];
    // 分界线
    NSString *MPboundary = [[NSString alloc]initWithFormat:@"--%@",TWITTERFON_FORM_BOUNDARY];
    // 结束符
    NSString *endMPboundary=[[NSString alloc]initWithFormat:@"%@--",MPboundary];
    
    // http body 的字符串
    NSMutableString *body = [[NSMutableString alloc] init];
    
    // 参数的集合的所有key的集合
    NSArray *keys= [parameterDict allKeys];
    // 遍历keys
    for(int i = 0; i < [keys count]; i++) {
        // 得到当前key
        NSString *key=[keys objectAtIndex:i];
        // 添加分界线,换行
        [body appendFormat:@"%@\r\n",MPboundary];
        // 添加字段名称,换2行
        [body appendFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",key];
        // 添加字段的值
        [body appendFormat:@"%@\r\n",[parameterDict objectForKey:key]];
    }
    // 添加分界线,换行
    [body appendFormat:@"%@\r\n",MPboundary];
    // 声明pic字段,文件名为boris.png
    [body appendFormat:@"%@", [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"%@\"\r\n",@"MyFilename"]];
    // 声明上传文件的格式
    [body appendFormat:@"Content-Type: image/png\r\n\r\n"];
    NSString *paraString = [dataArray componentsJoinedByString:@","];
    
    NSString *end=[[NSString alloc]initWithFormat:@"\r\n%@",endMPboundary];
    
    // 声明myRequestData,用来放入http body
    NSMutableData *myRequestData = [NSMutableData data];
    // 将body字符串转化为UTF8格式的二进制
    [myRequestData appendData:[body dataUsingEncoding:NSUTF8StringEncoding]];
    // data加入
    [myRequestData appendData:[paraString dataUsingEncoding:NSUTF8StringEncoding]];
    // 加入结束符--AaB03x--
    [myRequestData appendData:[end dataUsingEncoding:NSUTF8StringEncoding]];
    
    // 设置HTTPHeader中Content-Type的值
    NSString *content = [[NSString alloc]initWithFormat:@"multipart/form-data; boundary=%@",TWITTERFON_FORM_BOUNDARY];
    [request setValue:content forHTTPHeaderField:@"Content-Type"];
    // 设置Content-Length
    [request setValue:[NSString stringWithFormat:@"%d", (int)[myRequestData length]] forHTTPHeaderField:@"Content-Length"];
    
    // 设置http body
    [request setHTTPBody:myRequestData];
    // http method
    [request setHTTPMethod:@"POST"];
    // 连接(NSURLSession)
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error) {
            
        } else {
            if (success) {
                success(@"");
            }
        }
        NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"body:%@", str);
    }];
    [dataTask resume];
}

相关文章:

参考文档: