分享

从Samples中入门IOS开发(五)------ 基于HTTP的网络编程

lijian123841314 发表于 2013-12-15 23:52:57 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 0 5013
上一篇讲的是如何通过socket进行网络传输,实际上对于互联网上的资源,我们更多的是基于http来开发,SimpleURLConnections展示了如何基于http来进行数据传输,这里主要是讲client如何向http服务器请求和传输数据,http服务器端的实现不在此例子范围之内,实际上就是普通的http服务器。
从本例中主要能学到三点:
  • 基于Get下载文件
  • 基于Put上传文件
  • 基于Post上传文件基于Get下载文件
    首先通过URL打开Connection:
        request = [NSURLRequest requestWithURL:url];
        assert(request != nil);
       
        self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
        assert(self.connection != nil);
    然后实现NSURLConnectionDelegate来处理数据传输,其中实现下载图片到本地文件的方法如下:
    - (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)data
        // A delegate method called by the NSURLConnection as data arrives.  We just
        // write the data to the file.
    {
        #pragma unused(theConnection)
        NSInteger       dataLength;
        const uint8_t * dataBytes;
        NSInteger       bytesWritten;
        NSInteger       bytesWrittenSoFar;
        assert(theConnection == self.connection);
       
        dataLength = [data length];
        dataBytes  = [data bytes];
        bytesWrittenSoFar = 0;
        do {
            bytesWritten = [self.fileStream write:&dataBytes[bytesWrittenSoFar] maxLength:dataLength - bytesWrittenSoFar];
            assert(bytesWritten != 0);
            if (bytesWritten == -1) {
                [self stopReceiveWithStatus:@"File write error"];
                break;
            } else {
                bytesWrittenSoFar += bytesWritten;
            }
        } while (bytesWrittenSoFar != dataLength);
    }
    基于Put上传文件

    Put和Get类似,只不过文件上传是通过设置HTTP header来完成的:
    self.fileStream = [NSInputStream inputStreamWithFileAtPath:filePath];
    assert(self.fileStream != nil);
    // Open a connection for the URL, configured to PUT the file.
    request = [NSMutableURLRequest requestWithURL:url];
    assert(request != nil);
    [request setHTTPMethod:@"PUT"];
    [request setHTTPBodyStream:self.fileStream];
    if ( [filePath.pathExtension isEqual:@"png"] ) {
        [request setValue:@"image/png" forHTTPHeaderField:@"Content-Type"];
    } else if ( [filePath.pathExtension isEqual:@"jpg"] ) {
        [request setValue:@"image/jpeg" forHTTPHeaderField:@"Content-Type"];
    } else if ( [filePath.pathExtension isEqual:@"gif"] ) {
        [request setValue:@"image/gif" forHTTPHeaderField:@"Content-Type"];
    } else {
        assert(NO);
    }
    contentLength = (NSNumber *) [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] objectForKey:NSFileSize];
    assert( [contentLength isKindOfClass:[NSNumber class]] );
    [request setValue:[contentLength description] forHTTPHeaderField:@"Content-Length"];
    self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
    其中最主要的是设置三个header属性:method, body和contentLength
    [request setHTTPMethod:@"PUT"];
    [request setHTTPBodyStream:self.fileStream];
    [request setValue:[contentLength description] forHTTPHeaderField:@"Content-Length"];
    基于Post上传文件

    基于Post上传文件与Put最大不同点是,http body里除了放入文件本身的数据流之外,还得在文件数据流前后放入结构性的描述信息,比如之前:
    bodyPrefixStr = [NSString stringWithFormat:
                @
                // empty preamble
                "\r\n"
                "--%@\r\n"
                "Content-Disposition: form-data; name=\"fileContents\"; filename=\"%@\"\r\n"
                "Content-Type: %@\r\n"
                "\r\n"
    之后:
    bodySuffixStr = [NSString stringWithFormat:
                @
                "\r\n"
                "--%@\r\n"
                "Content-Disposition: form-data; name=\"uploadButton\"\r\n"
                "\r\n"
                "Upload File\r\n"
                "--%@--\r\n"
                "\r\n"
                //empty epilogue
                ,
                boundaryStr,
                boundaryStr
            ];
    因此,这里就需要能够把这些数据加入到文件流中,本例采用的方案是生产者和消费者的模式,把这两段信息拼接到文件流中:
    [NSStream createBoundInputStream:&consStream outputStream:&prodStream bufferSize:32768];
    assert(consStream != nil);
    assert(prodStream != nil);
    self.consumerStream = consStream;
    self.producerStream = prodStream;
    self.producerStream.delegate = self;
    [self.producerStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [self.producerStream open];
    // Set up our state to send the body prefix first.
    self.buffer      = [self.bodyPrefixData bytes];
    self.bufferLimit = [self.bodyPrefixData length];
    // Open a connection for the URL, configured to POST the file.
    request = [NSMutableURLRequest requestWithURL:url];
    assert(request != nil);
    [request setHTTPMethod:@"POST"];
    [request setHTTPBodyStream:self.consumerStream];
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=\"%@\"", boundaryStr] forHTTPHeaderField:@"Content-Type"];
    [request setValue:[NSString stringWithFormat:@"%llu", bodyLength] forHTTPHeaderField:@"Content-Length"];
    self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
    然后在handleevent中处理数据流的拼接:
    - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
        // An NSStream delegate callback that's called when events happen on our
        // network stream.
    {
        #pragma unused(aStream)
        assert(aStream == self.producerStream);
        switch (eventCode) {
            case NSStreamEventOpenCompleted: {
                // NSLog(@"producer stream opened");
            } break;
            case NSStreamEventHasBytesAvailable: {
                assert(NO);     // should never happen for the output stream
            } break;
            case NSStreamEventHasSpaceAvailable: {
                // Check to see if we've run off the end of our buffer.  If we have,
                // work out the next buffer of data to send.
                
                if (self.bufferOffset == self.bufferLimit) {
                    // See if we're transitioning from the prefix to the file data.
                    // If so, allocate a file buffer.
                   
                    if (self.bodyPrefixData != nil) {
                        self.bodyPrefixData = nil;
                        assert(self.bufferOnHeap == NULL);
                        self.bufferOnHeap = malloc(kPostBufferSize);
                        assert(self.bufferOnHeap != NULL);
                        self.buffer = self.bufferOnHeap;
                        
                        self.bufferOffset = 0;
                        self.bufferLimit  = 0;
                    }
                   
                    // If we still have file data to send, read the next chunk.
                   
                    if (self.fileStream != nil) {
                        NSInteger   bytesRead;
                        
                        bytesRead = [self.fileStream read:self.bufferOnHeap maxLength:kPostBufferSize];
                        
                        if (bytesRead == -1) {
                            [self stopSendWithStatus:@"File read error"];
                        } else if (bytesRead != 0) {
                            self.bufferOffset = 0;
                            self.bufferLimit  = bytesRead;
                        } else {
                            // If we hit the end of the file, transition to sending the
                            // suffix.
                            [self.fileStream close];
                            self.fileStream = nil;
                            
                            assert(self.bufferOnHeap != NULL);
                            free(self.bufferOnHeap);
                            self.bufferOnHeap = NULL;
                            self.buffer       = [self.bodySuffixData bytes];
                            self.bufferOffset = 0;
                            self.bufferLimit  = [self.bodySuffixData length];
                        }
                    }
                   
                    if ( (self.bufferOffset == self.bufferLimit) && (self.producerStream != nil) ) {
                        self.producerStream.delegate = nil;
                        [self.producerStream close];
                    }
                }
                
                // Send the next chunk of data in our buffer.
                
                if (self.bufferOffset != self.bufferLimit) {
                    NSInteger   bytesWritten;
                    bytesWritten = [self.producerStream write:&self.buffer[self.bufferOffset] maxLength:self.bufferLimit - self.bufferOffset];
                    if (bytesWritten
    基于Post的数据传输看上去很复杂,实际上道理还是很简单,重点就在于数据流的拼接上。
  • 没找到任何评论,期待你打破沉寂

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    关闭

    推荐上一条 /2 下一条