深入解析POST上传-->AFNetworking的底层理解

一. POST单文件上传-简单使用

1. 创建请求

  • 实例化请求并设置基本参数
    // 0. 获取服务器端口的地址
    NSURL *url = [NSURL URLWithString:@"http://localhost/upload/upload.php"];

    #warning:对于POST请求,必须手动设置其请求方法,因此要使用可变请求
    // 1. 创建请求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    // 2. 设置请求方式
    request.HTTPMethod = @"POST";

    // 3. 告诉服务器本次上传文件的相关信息
    // 固定格式: 设置Content-Type
    // Content-Type: multipart/form-data; boundary=---------------------------198596859919834017191791522499
    // Content-Type:本次上传文件类型信息,包含boundary
    // boundary:本次上传文件的边界(自己随意设置,只要三个地方一致即可)
    NSString *type = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", kBoundary];
    [request setValue:type forHTTPHeaderField:@"Content-Type"];
  • 重难点: 设置请求体,分为三个部分
    • 上边界部分,告诉服务器要做数据上传,包含了

      • userfile -> 负责上传文件脚本中的 字段名,开发的时候,可以咨询后端程序员
      • filename -> 将文件保存在服务器上的文件名称
      • Content-Type -> 客户端告诉服务器上传文件的文件类型(如果不想写文件类型,统一用 application/octet-stream[8进制流])
    • 上传文件的数据部分(即文件内容的二进制数据)

    • 下边界部分,严格按照字符串格式来设置:--boundary--

    // 实例化请求体
    NSMutableData *data = [NSMutableData data];

//    -----------------------------198596859919834017191791522499
//    Content-Disposition: form-data; name="userfile"; filename="WPFNetWorkTool.h"
//    Content-Type: application/octet-stream
#warning 有些服务器可以直接使用 \n,但是新浪微博如果使用 \n 上传文件,服务器会返回“没有权限”的错误! 因此一定要注意安全换行:\r\n
    // 1. 拼接上传文件的上边界信息
    NSMutableString *headerStrM = [NSMutableString stringWithFormat:@"\r\n--%@\r\n", kBoundary];

    // name=%@ :服务器接收参数的key值,后台工作人员告诉我们
    // filename=%@ :文件上传到服务器的存储名,若不设置则为默认名,名称保持不变
    [headerStrM appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n", @"userfile", @"123" ];

    // Content-Type: application/octet-stream 表明文件的上传类型,乱写类型不会影响上传,但是不符合规范
    [headerStrM appendString:@"Content-Type: application/octet-stream\r\n\r\n"];

    // 将上传文件的上边界信息添加到请求体中
    [data appendData:[headerStrM dataUsingEncoding:NSUTF8StringEncoding]];

    // 2. 设置文件内容
    // 文件地址
    NSString *filePath = @"/Users/wangpengfei/Desktop/WPFNetWorkTool.h";

    // 将文件转化为二进制形式
    NSData *fileData = [NSData dataWithContentsOfFile:filePath];

    // 将文件内容添加到请求体中
    [data appendData:fileData];

    // 3. 设置文件的下边界
    // -----------------------------198596859919834017191791522499--
    NSString *footerStrM = [NSString stringWithFormat:@"\r\n--%@--", kBoundary];

    NSLog(@"footerStrM--->%@", footerStrM);

    // 将下边界添加到请求体中
    [data appendData:[footerStrM dataUsingEncoding:NSUTF8StringEncoding]];

    // 4. 设置请求体
    request.HTTPBody = data;

常见的 Content-Type 类型:

大类型 小类型
image png
image jpg
image gif
text html
application json

2. 发送请求

[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        /*
        打印结果:
            {"userfile":{"name":"123","type":"application\/octet-stream","tmp_name":"\/private\/var\/tmp\/phpEk0KCK","error":0,"size":0}}
        */

    }] resume];

二. POST单文件上传-简单封装结构体

1. 获得本地文件响应头信息,使用同步方法*重难点*

通过响应头信息,可以获得文件的类型/长度/建议的名称.
  • MIMEType :就是文件类型
  • suggestedFilename : 推荐文件名(本地存储名)
  • expectedContentLength : 文件长度

如果文件比较大,不建议发送本地请求.发送本地请求,会将文件从沙盒中加载到内存中,造成内存开销.

    - (NSURLResponse *)getFileResponseWithFilePath:(NSString *)filePath {
    // 动态获取文件类型

    // 1. 获取文件路径,根据路径获取 url 地址,本地协议名  file://
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"file://%@", filePath]];

    // 2. 创建请求
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    // NSURLSession 没有同步请求的方法
    // 利用 NSURLConnection 发送同步请求

    // 定义一片空的地址
    NSURLResponse *response = nil;

    // &response 二级指针
    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];

    return response;
}

2. 封装请求体格式

  • filePath : 上传文件的路径
  • fileKey : 服务器接受文件的 key 值
  • fileName : 上传文件在服务器中保存的名称(可选)
    - (NSData *)setupHttpBodyWithFilePath:(NSString *)filePath fileKey:(NSString *)fileKey fileName:(NSString *)fileName;
  • 当用户没有设置fileName时,调用方法一:设置文件为默认名
    // 调用获得本地文件信息的方法
    NSURLResponse *response = [self getFileResponseWithFilePath:filePath];

    if (!fileName) {
        fileName = response.suggestedFilename;
    }

三. POST单文件上传封装

1. 取出已封装好的单例类WPFNetWorkTool,封装以下方法

  • urlString:网络接口
  • filePath:需要上传的文件路径
  • fileKey:服务器接收用户上传文件的key值
  • fileName:上传文件存储到服务器的名称
  • success:上传成功时调用的block
  • fail:上传失败时调用的block
    - (void)POSTFileWithUrlString:(NSString *)urlString
     filePath:(NSString *)filePath fileKey:(NSString *)fileKey
     fileName:(NSString *)fileName
     success:(successBlock)success fail:(failBlock)fail;

2. 在发送请求方法中增加以下内容

[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        // 成功
        if (data && !error) {
            id responseObj = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
            // 如果不能解析JSON数据
            if (!responseObj) {
                responseObj = data;
            }
            // 执行回调
            if (success) {
                success(responseObj, response);
            }
        // 失败
        } else {
            // 执行回调
            if (fail) {
                fail(error);
            }
        }
    }] resume];

3. 封装方法的调用

// POST文件上传
[[WPFNetWorkTool sharedTool] POSTFileWithUrlString:@"http://localhost/upload/upload.php" filePath:@"/Users/wangpengfei/Desktop/葵花宝典/下载工具/WPFNetWorkTool/WPFNetWorkTool.h" fileKey:@"userfile" fileName:NULL success:^(id obj, NSURLResponse *response) {
    NSLog(@"obj--->%@", obj);
} fail:^(NSError *error) {
    NSLog(@"error--->%@", error);
}];

#warning 图片等文件可以显示name,但是oc程序文件不能显示
/*
打印结果:
    obj--->{
    userfile =     {
        error = 0;
        name = "(null)";
        size = 1702;
        "tmp_name" = "/private/var/tmp/phposR0Tv";
        type = "application/octet-stream";
    };
}
*/

四. POST多文件上传-简单使用

多文件上传和单文件上传的基本思路是一样的,唯一的区别在于对请求体的封装.

  • 多文件上传的请求体格式

      // 第一个文件上边界及参数
      \r\n--boundary\r\n
      Content-Disposition: form-data; name=userfile[]; filename=美女\r\n
      Content-Type:image/jpeg\r\n\r\n
    
      第一个文件的二进制数据部分
    
      // 第二个文件上边界及参数
      \r\n--boundary\r\n
      Content-Disposition: form-data; name=userfile[]; filename=JSON\r\n
      Content-Type:text/plain\r\n\r\n
    
      第二个文件的二进制数据部分
    
      // 下边界
      \r\n--boundary--
    
  • 1.设置第一个文件的上边界及参数

    NSMutableString *headerStrM1 = [NSMutableString stringWithFormat:@"\r\n--%@\r\n", kBoundary];

    // name=%@ :服务器接收参数的key值,后台工作人员告诉我们
    // filename=%@ :文件上传到服务器的存储名,若不设置则为默认名,名称保持不变
#warning @"userfile[]"后台人员提供的数据
    [headerStrM1 appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n", @"userfile[]", @"234" ];

    // Content-Type: application/octet-stream 表明文件的上传类型,乱写类型不会影响上传,但是不符合规范
    [headerStrM1 appendString:@"Content-Type: application/octet-stream\r\n\r\n"];

    // 将上传文件的上边界信息添加到请求体中
    [data appendData:[headerStrM1 dataUsingEncoding:NSUTF8StringEncoding]];
  • 2.设置第一个文件的二进制数据
    // 文件地址
    NSString *filePath1 = @"/Users/wangpengfei/Desktop/photo/IMG_5544.jpg";

    // 将文件转化为二进制形式
    NSData *fileData1 = [NSData dataWithContentsOfFile:filePath1];

    // 将文件内容添加到请求体中
    [data appendData:fileData1];
  • 3.设置第二个文件的上边界及参数
NSMutableString *headerStrM2 = [NSMutableString stringWithFormat:@"\r\n--%@\r\n", kBoundary];

    // name=%@ :服务器接收参数的key值,后台工作人员告诉我们
    // filename=%@ :文件上传到服务器的存储名,若不设置则为默认名,名称保持不变

    [headerStrM2 appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n", @"userfile[]", @"123" ];

    [headerStrM2 appendString:@"Content-Type: application/octet-stream\r\n\r\n"];

    // 将上传文件的上边界信息添加到请求体中
    [data appendData:[headerStrM2 dataUsingEncoding:NSUTF8StringEncoding]];
  • 4.设置第二个文件的二进制数据
// 文件地址
    NSString *filePath2 = @"/Users/wangpengfei/Desktop/photo/beauty1.jpg";

    // 将文件转化为二进制形式
    NSData *fileData2 = [NSData dataWithContentsOfFile:filePath2];

    // 将文件内容添加到请求体中
    [data appendData:fileData2];
  • 5.设置下边界
    NSString *footerStrM = [NSString stringWithFormat:@"\r\n--%@--", kBoundary];

    // 将下边界添加到请求体中
    [data appendData:[footerStrM dataUsingEncoding:NSUTF8StringEncoding]];

五. POST多文件上传-添加普通参数

  • 有些网络请求,客户端需要告诉服务器一些必要的数据,服务器根据客户端传过来的数据(参数)去数据库检索出对应的数据.返回给客户端.
  • 必须参数: 必须附带的参数(登录时候的账号和密码).
  • 可选参数: 可以自由选择是否告诉给服务器的参数.
  • 典型应用:
    • 新浪微博: 上传图片的同时,发送一条微博信息!
    • 购物评论: 购买商品之后发表评论的时候图片+评论内容!
  • 多个参数之间以 & 分割.参数是'无序'的.
  • 大公司: 能够附带参数,就会尽量多的附带参数.网络监测大数据开发/页面检测都必须由客户端发送参数给服务器.

普通参数的格式如下:

-----------------------------16778832101575341713442286528
Content-Disposition: form-data; name="username"

Wpf

普通参数添加的位置:最后一个文件的二进制内容下边界之间

使用一个可变字符串连接所有参数的全部信息(上边界和具体内容),然后统一转化为二进制形式

  • 设置第一个参数的上边界
    NSMutableString *parameterStr = [NSMutableString stringWithFormat:@"\r\n--%@\r\n", kBoundary];
    // name=%@ :服务器接收普通文本参数的key值.后端人员告诉我们.
    // 文本参数也有可能有多个...
    [parameterStr appendFormat:@"Content-Disposition: form-data; name=%@\r\n\r\n", @"username"];
  • 设置第一个参数的内容
    [parameterStr appendString:@"WangPengfei"];
  • 设置第二个参数的上边界
    [parameterStr appendFormat:@"\r\n--%@\r\n", kBoundary];
    [parameterStr appendFormat:@"Content-Disposition: form-data; name=%@\r\n\r\n", @"password"];
  • 设置第二个参数的内容
    [parameterStr appendString:@"123321"];
  • 统一转化为二进制形式
    [data appendData:[parameterStr dataUsingEncoding:NSUTF8StringEncoding]];

六. 简单封装

1. 将文件名和文件地址参数key值和参数具体值分别封装为字典

  • 设置文件字典
    // 设置上传文件在服务器存储的名称
    NSString *name1 = @"photo.jpg";
    NSString *name2 = @"math.h";
    NSString *name3 = @"video.json";

    // 设置文件地址
    NSString *filePath1 = @"/Users/wangpengfei/Desktop/IMG_5097.jpg";
    NSString *filePath2 = @"/Users/wangpengfei/Desktop/Math.m";
    NSString *filePath3 = @"/Users/wangpengfei/Desktop/vedios.json";

    NSDictionary *fileDict = @{
                               name1:filePath1,
                               name2:filePath2,
                               name3:filePath3
                               };
  • 设置普通文本参数字典
    NSDictionary *parameters = @{
                                 @"username":@"wpf",
                                 @"password":@"12300",
                                 @"age":@"24"
                                 };

2. 方法的封装

  • 方法名
    - (NSData *)getHttpBodyWithFileKey:(NSString *)fileKey fileDict:(NSDictionary *)fileDict parameters:(NSDictionary *)parameters
  • 遍历文件字典
    // 遍历文件参数字典,取出文件字典中的 key值 和 value 值
    [fileDict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {

        // 上传文件在服务器中保存的名称
        NSString *fileName = key;
        // 上传文件在本地的路径
        NSString *filePath = obj;

        // 上传文件的请求体格式
        // 1. 文件的上边界
        // 1.1 获取文件上边界的字符串
        NSMutableString *headerStrM1 = [NSMutableString stringWithFormat:@"\r\n--%@\r\n", kBoundary];

        [headerStrM1 appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n", fileKey, fileName];

        [headerStrM1 appendFormat:@"Content-Type: %@\r\n\r\n", @"application/octet-stream"];

        // 1.2 将字符串转为二进制数据,并添加到请求体中
        [data appendData:[headerStrM1 dataUsingEncoding:NSUTF8StringEncoding]];

        // 2. 获取文件的二进制数据,并添加到请求体中
        [data appendData:[NSData dataWithContentsOfFile:filePath]];
    }];
  • 遍历普通文本参数字典
    [parameters enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        //  参数格式:
        //    -----------------------------16778832101575341713442286528
        //    Content-Disposition: form-data; name="username"
        //
        //    Wpf

        NSString *parameterKey = key;
        NSString *parameterValue = obj;

        // 1. 普通参数的上边界
        NSMutableString *parameterStr = [NSMutableString stringWithFormat:@"\r\n--%@\r\n", kBoundary];
        // name=%@ :服务器接收普通文本参数的key值.后端人员告诉我们.
        // 文本参数也有可能有多个...
        [parameterStr appendFormat:@"Content-Disposition: form-data; name=%@\r\n\r\n", parameterKey];

        // 2. 第一个普通参数的内容
        [parameterStr appendString:parameterValue];

        [data appendData:[parameterStr dataUsingEncoding:NSUTF8StringEncoding]];
    }];

如果在 iOS 中,要实现POST上传文件,需要按照上述格式,拼接数据!

因为:格式是 W3C 指定的标准格式,苹果没有做任何封装!其他语言,都做了封装!

3. 方法的调用

  • 设置请求体
    request.HTTPBody = [self getHttpBodyWithFileKey:@"userfile[]" fileDict:fileDict parameters:parameters];

七. POST多文件上传方法的封装

  • 基本参数
  • urlString:网络接口
  • fileKey:服务器接收用户上传文件的key值
  • fileDict:文件字典
  • parameters:普通文本参数的字典
  • success:上传成功时调用的block
  • fail:上传失败时调用的block
  • 方法名
    - (void)POSTMoreFileWithUrlString:(NSString *)urlString
     fileKey:(NSString *)fileKey fileDict:(NSDictionary *)fileDict
     parameters:(NSDictionary *)parameters
     success:(successBlock)success fail:(failBlock)fail;
  • 其他改动同POST单文件的深入封装-上传封装

八. AFN-第三方框架的使用

  • AFN 能够同时实现上传一个文件,有些格式的文件,用 AFN 无法上传!
  • ASI 能够同时实现上传多个文件,MRC的,2012年就停止更新了,设计的目标平台, iOS 2.0/iOS 3.0 !

1. 上传文件

    // 1. 创建管理者
    AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];

    // 2. 发送请求
    [mgr POST:@"http://localhost/upload/upload.php" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
        // formData :设置上传文件所需要的参数,两种上传方法:
        // <1> 通过本地文件的 url 上传
        {
            NSString *fromFile = @"/Users/wangpengfei/Desktop/meinv.jpg";

            NSURL *url = [NSURL URLWithString:@"file:///Users/wangpengfei/Desktop/IMG_5544.jpg"];
            // url :需要上传文件的文件路径
            // name :服务器接收的文件名.
            // fileName: 文件在服务器中保存的名字
            // mimeType : 文件类型
            [formData appendPartWithFileURL:url name:@"userfile" fileName:@"beauty.jpg" mimeType:@"image/jpg" error:NULL];
        }
        // <2> 通过文件的 二进制数据 上传
        {
            NSData *data = [NSData dataWithContentsOfFile:zipFile];

            [formData appendPartWithFileData:data name:@"userfile" fileName:@"beauty.zip" mimeType:@"gzip"];
        }

        } success:^(AFHTTPRequestOperation *operation, id responseObject) {
            // 上传成功之后的回调
            NSLog(@"%@",responseObject);

        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            //  上传失败之后的回调
            NSLog(@"error-->%@", error);
    }];

2. 监测网络状态

    // 创建 网络状态管理者
    AFNetworkReachabilityManager *mgr = [AFNetworkReachabilityManager sharedManager];

    // 监测网络状态的改变
    [mgr setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        // 当网络状态发生改变的时候调用这个block
        switch (status) {
            case AFNetworkReachabilityStatusReachableViaWiFi:
            NSLog(@"WIFI网络");
            break;

            case AFNetworkReachabilityStatusReachableViaWWAN:
            NSLog(@"蜂窝网络");
            break;

            case AFNetworkReachabilityStatusNotReachable:
            NSLog(@"没有网络");
            break;

            case AFNetworkReachabilityStatusUnknown:
            NSLog(@"未知网络");
            break;
            default:
                break;
        }
    }];

    // 开始监控
    [mgr startMonitoring];

3. Reachability 监测网络状态(第三方框架)

  • 注册通知观察者,网络状态改变时,接收通知!
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(InternetStatusChanged) name:kReachabilityChangedNotification object:nil];

    // 控制器销毁时,移除通知观察者.
    -(void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
  • 根据当前网络状态,做出不同的响应.
    - (void)InternetStatusChanged
    {
        NSLog(@"网络状态改变了");

        if ([Reachability reachabilityForLocalWiFi].currentReachabilityStatus == ReachableViaWiFi) {
            NSLog(@"Wifi 网络");
        }
        if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus == ReachableViaWWAN) {
            NSLog(@"蜂窝移动网络");
        }
        if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus == NotReachable)
        {
            NSLog(@"没有网络");
        }
    }
  • 创建 Reachability 对象,开始监测网络状态的改变
    - (void)MonitorInternetStatus
    {
        Reachability *reachability = [Reachability reachabilityForInternetConnection];

        [reachability startNotifier];

        self.reachability = reachability;
    }

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        [self MonitorInternetStatus];
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • /*----------------- 01 POST上传单个文件 -----------------*/ 重点:...
    蓝心儿的蓝色之旅阅读 2,257评论 0 5
  • iOS开发系列--网络开发 概览 大部分应用程序都或多或少会牵扯到网络开发,例如说新浪微博、微信等,这些应用本身可...
    lichengjin阅读 3,637评论 2 7
  • 小文件下载如果文件比较小,下载方式会比较多直接用NSData的+ (id)dataWithContentsOfUR...
    醉叶惜秋阅读 850评论 0 0
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,850评论 6 13
  • 我想多年后 我也会如现在般的累或者是超过如今这般 但也依旧在鼓励自己要在坚持下再努力下再把生活变得充实一些再阳光一...
    柒亓玘呮阅读 107评论 0 0