• iOS 实现简单的Http 服务


    http 是计算机之间通讯协议的比较简单的一种。在iPhone上,由于没有同步数据和文件共享,所以实现PC与设备之间的数据传输的最佳方式就是在程序中嵌套一个http 服务器。在这篇帖子中,我将简单的演示第三方的http 服务器的使用。

    示例程序运行如下(在PC端输入设备的IP和端口,便可选取当前PC的文件进行上传至当前的设备)

    该服务器涉及的相关类如下图所示

    代码实现,创建一个自己需求的HttpConnection类继承于第三方的HttpConnection,代码如下

    MyHTTPConnection.h

    #import "HTTPConnection.h"

    @class MultipartFormDataParser;

    @interface MyHTTPConnection : HTTPConnection

    {

        MultipartFormDataParser * parser;

        NSFileHandle * storeFile;

        NSMutableArray * uploadedFiles;

    }

    @end

     MyHTTPConnection.m

    #import "MyHTTPConnection.h"

    #import "HTTPMessage.h"

    #import "HTTPDataResponse.h"

    #import "DDNumber.h"

    #import "HTTPLogging.h"

    #import "MultipartFormDataParser.h"

    #import "MultipartMessageHeaderField.h"

    #import "HTTPDynamicFileResponse.h"

    #import "HTTPFileResponse.h"

    static const int httpLogLevel = HTTP_LOG_LEVEL_VERBOSE;

    @interface MyHTTPConnection ()

    {

        float totalSize;

        float progress;

    }

    @end

    @implementation MyHTTPConnection

     个人需要,1-8的方法都有实现

    1:- (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path

    {

        NSLog(@"%s",__FUNCTION__);

        HTTPLogTrace();

        if (  [ method isEqualToString:@"POST"] ) {

            if ( [path isEqualToString:@"/upload.html"] ) {

                return YES;

            }

        }

        return  [super supportsMethod:method atPath:path];

    }

    2:-(BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path{

    if([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.html"]) {

            // here we need to make sure, boundary is set in header

            NSString* contentType = [request headerField:@"Content-Type"];

            NSUInteger paramsSeparator = [contentType rangeOfString:@";"].location;

            if( NSNotFound == paramsSeparator ) {

                return NO;

            }

            if( paramsSeparator >= contentType.length - 1 ) {

                return NO;

            }

            NSString* type = [contentType substringToIndex:paramsSeparator];

            if( ![type isEqualToString:@"multipart/form-data"] ) {

                // we expect multipart/form-data content type

                return NO;

            }

            

    // enumerate all params in content-type, and find boundary there

            NSArray* params = [[contentType substringFromIndex:paramsSeparator + 1] componentsSeparatedByString:@";"];

            for( NSString* param in params ) {

                paramsSeparator = [param rangeOfString:@"="].location;

                if( (NSNotFound == paramsSeparator) || paramsSeparator >= param.length - 1 ) {

                    continue;

                }

                NSString* paramName = [param substringWithRange:NSMakeRange(1, paramsSeparator-1)];

                NSString* paramValue = [param substringFromIndex:paramsSeparator+1];

                

                if( [paramName isEqualToString: @"boundary"] ) {

                    // let's separate the boundary from content-type, to make it more handy to handle

                    [request setHeaderField:@"boundary" value:paramValue];

                }

            }

            // check if boundary specified

            if( nil == [request headerField:@"boundary"] )  {

                return NO;

            }

            return YES;

        }

        

    return [super expectsRequestBodyFromMethod:method atPath:path];

    }

    3:- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path{

    if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.html"])

    {

        

    // this method will generate response with links to uploaded file

    NSMutableString* filesStr = [[NSMutableString alloc] init];

            

    for( NSString* filePath in uploadedFiles ) {

    //generate links

    [filesStr appendFormat:@"%@<br/><br/>", [filePath lastPathComponent]];

    }

    NSString* templatePath = [[config documentRoot] stringByAppendingPathComponent:@"upload.html"];

    NSDictionary* replacementDict = [NSDictionary dictionaryWithObject:filesStr forKey:@"MyFiles"];

    // use dynamic file response to apply our links to response template

    return [[HTTPDynamicFileResponse alloc] initWithFilePath:templatePath forConnection:self separator:@"%" replacementDictionary:replacementDict];

    }

    if( [method isEqualToString:@"GET"] && [path hasPrefix:@"/upload/"] ) {

    // let download the uploaded files

    return [[HTTPFileResponse alloc] initWithFilePath: [[config documentRoot] stringByAppendingString:path] forConnection:self];

    }

    return [super httpResponseForMethod:method URI:path];

    }

    4:- (void)prepareForBodyWithSize:(UInt64)contentLength{

    HTTPLogTrace();

        totalSize = contentLength/1000.0/1000.0;

    NSLog(@"%f",contentLength/1000.0/1000.0);

    // set up mime parser

        NSString* boundary = [request headerField:@"boundary"];

        parser = [[MultipartFormDataParser alloc] initWithBoundary:boundary formEncoding:NSUTF8StringEncoding];

        parser.delegate = self;

        

    uploadedFiles = [[NSMutableArray alloc] init];

    }

    5:- (void)processBodyData:(NSData *)postDataChunk{

    HTTPLogTrace();

        progress += postDataChunk.length/1024.0/1024.0;

        NSLog(@"%f",progress);

        NSString * temp  = [ NSString stringWithFormat:@"上传进度:%.2fMB/%.2fMB",progress,totalSize ];

        dispatch_async(dispatch_get_main_queue(), ^{

             [SVProgressHUD showProgress:progress/totalSize status:temp maskType:SVProgressHUDMaskTypeBlack];

        });

        // append data to the parser. It will invoke callbacks to let us handle

        // parsed data.

        [parser appendData:postDataChunk];

    }

    6:- (void) processStartOfPartWithHeader:(MultipartMessageHeader*) header{

     NSLog(@"%s",__FUNCTION__);

        MultipartMessageHeaderField* disposition = [header.fields objectForKey:@"Content-Disposition"];

    NSString* filename = [[disposition.params objectForKey:@"filename"] lastPathComponent];

        NSLog(@"####filename=%@",filename);

        if ( (nil == filename) || [filename isEqualToString: @""] ) {

            // it's either not a file part, or

    // an empty form sent. we won't handle it.

    return;

    }

         

        NSString* uploadDirPath = [self CreatreTempDir] ;

        

       // NSLog(@"uploadDirPath%@",[self CreatDir]);

    BOOL isDir = YES;

    if (![[NSFileManager defaultManager]fileExistsAtPath:uploadDirPath isDirectory:&isDir ]) {

    [[NSFileManager defaultManager]createDirectoryAtPath:uploadDirPath withIntermediateDirectories:YES attributes:nil error:nil];

    }

        NSString * filePath = [uploadDirPath stringByAppendingPathComponent:filename];

        NSFileManager * fileManager = [NSFileManager defaultManager];

    if( [[NSFileManager defaultManager] fileExistsAtPath:filePath] ) {

            storeFile = nil;

        }

        else {

    HTTPLogVerbose(@"Saving file to %@", filePath);

            NSError *error;

    if(![[NSFileManager defaultManager] createDirectoryAtPath:uploadDirPath withIntermediateDirectories:true attributes:nil error:&error]) {

    HTTPLogError(@"Could not create directory at path: %@----%@", filePath,error);

    }

    if(![[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil]) {

    HTTPLogError(@"Could not create file at path: %@", filePath);

    }

    storeFile = [NSFileHandle fileHandleForWritingAtPath:filePath];

    [uploadedFiles addObject: [NSString stringWithFormat:@"/upload/%@", filename]];

        }

    }

    7:- (void) processContent:(NSData*) data WithHeader:(MultipartMessageHeader*) header{

     if(!storeFile)

        {

            MultipartMessageHeaderField* disposition = [header.fields objectForKey:@"Content-Disposition"];

            NSString* filename = [[disposition.params objectForKey:@"filename"] lastPathComponent];

            NSLog(@"####filename=%@",filename);

            if ( (nil == filename) || [filename isEqualToString: @""] ) {

                // it's either not a file part, or

                // an empty form sent. we won't handle it.

                return;

            }

            NSString* uploadDirPath = [self CreatreTempDir] ;

            NSString * filePath = [uploadDirPath stringByAppendingPathComponent:filename];

            storeFile = [NSFileHandle fileHandleForWritingAtPath:filePath];

            [uploadedFiles addObject: [NSString stringWithFormat:@"/upload/%@", filename]];

        }

    if( storeFile ) {

    [storeFile writeData:data];

    }

    }

    8:- (void) processEndOfPartWithHeader:(MultipartMessageHeader*) header{

    dispatch_async(dispatch_get_main_queue(), ^{

            [SVProgressHUD dismiss];

        });

        progress = 0.f;

        

        MultipartMessageHeaderField* disposition = [header.fields objectForKey:@"Content-Disposition"];

    NSString* filename = [[disposition.params objectForKey:@"filename"] lastPathComponent] ;

       // NSLog(@"####END ---filename=%@",filename);

        NSString * tempFilePath = [[ self CreatreTempDir ] stringByAppendingPathComponent:filename];

        

        NSMutableArray *arraynumber = [NSMutableArray arrayWithContentsOfFile:[Global getChangePathName]];

        NSString *strnumber = [arraynumber lastObject];

        

        NSString * uploadFilePath = [[ self CreatDir ] stringByAppendingPathComponent: [[NSString stringWithFormat:@"%d.",[strnumber intValue] + 1 ] stringByAppendingString:filename]];

        

        [arraynumber addObject:[NSString stringWithFormat:@"%d", [strnumber intValue] + 1]];

        

        [arraynumber writeToFile:[Global getChangePathName] atomically:YES];

        BOOL result = [ self copyMissFile:tempFilePath toPath:uploadFilePath ];

        

        if (result) {

            NSLog(@"移动成功");

        }else{

            NSLog(@"移动失败");

        }

        

    [storeFile closeFile];

    storeFile = nil;

    }

    - (BOOL)copyMissFile:(NSString * )sourcePath toPath:(NSString *)toPath{

    BOOL retVal ;

        NSLog(@"%s",__FUNCTION__);

    //    if ( [fileManger fileExistsAtPath:toPath]  ) {

    //        [fileManger removeItemAtPath:toPath error:nil];

    //    }

        retVal = [[ NSFileManager defaultManager ] moveItemAtPath:sourcePath toPath:toPath error:nil];

        

        return retVal;

    }

    下面两个方法都是创建文件,大家自己可以随意定义

    -(NSString *)CreatDir;

    -(NSString *)CreatreTempDir;

    下面根据需求实现以下这些方法,我是在全局的类里面实现的

    Global.m

    + (BOOL)createiPhoneServer  //创建并设置iPhone的服务器

    {

        Global * global = [ Global sharedGlobal ];

        

        global.httpServer = [[HTTPServer alloc] init];

        [global.httpServer setType:@"_http._tcp."];

        NSString *docRoot = [[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"MyResources"] stringByAppendingPathComponent:@"Web"];

        BOOL  isDir  = YES ;

        if ( [[NSFileManager defaultManager] fileExistsAtPath:docRoot isDirectory:&isDir  ]  ) {

            NSLog(@"找到Web文件夹");

        }

        [global.httpServer setDocumentRoot:docRoot];

        [global.httpServer setPort:Http_Port];//本地端口

        [global.httpServer setConnectionClass:[MyHTTPConnection class]];//设置连接类为我们之前代码实现的连接类

        

        return YES;

    }

    //开启服务器

    + (BOOL)startServer

    {

        Global * global = [ Global sharedGlobal];

        NSError * error;

        

        

        if ( [global.httpServer start:&error] ) {

            NSLog(@"开启成功%u",[global.httpServer listeningPort]);

            return YES;

        }else

        {

            NSLog(@"开启失败,%@",error);

            return NO;

        }

        

    }

    + (BOOL)stopServer

    {

        [[Global sharedGlobal].httpServer stop:YES];

        return YES;

    }

     //获得当前设备的IP

    +(NSString *)getIPAddress

    {

        NSString *address = @"开启失败";

        struct ifaddrs *interfaces = NULL;

        struct ifaddrs *temp_addr = NULL;

        int success = 0;

        // retrieve the current interfaces - returns 0 on success

        success = getifaddrs(&interfaces);

        

        

        if (success == 0) {

            // Loop through linked list of interfaces

            temp_addr = interfaces;

            

            

            while(temp_addr != NULL) {

                if(temp_addr->ifa_addr->sa_family == AF_INET) {

                    // Check if interface is en0 which is the wifi connection on the iPhone

                    if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {

                        // Get NSString from C String

                        address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];

                    }

                }

                temp_addr = temp_addr->ifa_next;

            }

        }

        // Free memory

        freeifaddrs(interfaces);

        

        NSString * str = nil;

        if ( [address isEqualToString:@"开启失败"] ) {

            str = @"开启失败";

        }else {

            str = [NSString stringWithFormat:@"http://%@:%d",address, Http_Port ];

        }

        return str;

    }

     //获取当前设备的网络状态

    +(NSString *) getDeviceSSID

    {

        

        NSArray *ifs = (__bridge id)CNCopySupportedInterfaces() ;

        

        id info = nil;

        

        for (NSString *ifnam in ifs) {

            

            info = (__bridge id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);

            

            if (info && [info count]) {

                

                break;

                

            }

            

        }

        

        NSDictionary *dctySSID = (NSDictionary *)info;

        

        NSString *ssid = [ dctySSID objectForKey:@"SSID" ];

        

        

        ssid = ssid== nil?@"无网络":ssid;

        return ssid;

        

    }

     然后再你需要的地方开启这个服务即可,至此http 服务搭建完成,开启服务之后就可在PC的浏览器输入当前的设备IP地址和端口,如http://192.168.2.155:8088,然后上传所需的文件即可,根据程序设计,这些文件会存在相应的地方

  • 相关阅读:
    luogu 5311 [Ynoi2011]D1T3 动态点分治+树状数组
    LOJ #6145. 「2017 山东三轮集训 Day7」Easy 点分树+线段树
    BZOJ 2117: [2010国家集训队]Crash的旅游计划 动态点分治+二分
    BZOJ 1095: [ZJOI2007]Hide 捉迷藏 动态点分治+堆
    BZOJ 3924: [Zjoi2015]幻想乡战略游戏 动态点分治
    luogu 3241 [HNOI2015]开店 动态点分治+二分+vector
    luogu 2993 [FJOI2014]最短路径树问题 Dijkstra+点分治
    BZOJ 3697: 采药人的路径 点分治
    启动elasticsearch报错的几种原因及解决方法
    SpringBoot与MybatisPlus3.X整合示例(十六)
  • 原文地址:https://www.cnblogs.com/camillezlh/p/4262271.html
Copyright © 2020-2023  润新知