從Samples中入門IOS開發(五)—— 基於HTTP的網路編程

來源:互聯網
上載者:User

上一篇講的是如何通過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 <= 0) {                    [self stopSendWithStatus:@"Network write error"];                } else {                    self.bufferOffset += bytesWritten;                }            }        } break;        case NSStreamEventErrorOccurred: {            NSLog(@"producer stream error %@", [aStream streamError]);            [self stopSendWithStatus:@"Stream open error"];        } break;        case NSStreamEventEndEncountered: {            assert(NO);     // should never happen for the output stream        } break;        default: {            assert(NO);        } break;    }}

基於Post的資料轉送看上去很複雜,實際上道理還是很簡單,重點就在於資料流的拼接上。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.