(更新) 大きなファイル (ビデオまたは写真) を読み込んで、SOAP リクエスト経由でリモート サーバーに送信しようとしています。データを Base64 文字列としてエンコードする必要があります。私は次のようにこれをやろうとしています:
base64 でエンコードされたデータを「回避」する SOAP リクエスト用のテンプレート xml を作成します。
SOAP xml の最初の部分をバッファにプッシュする
ビデオ ファイルを開いてチャンクにエンコードし、エンコードされた各チャンクをバッファにプッシュします。
最後に、SOAP xml の 2 番目の部分をプッシュします。
上記のようにパーツを「キューに入れる」ことができるようにするために、バッファリング機能を備えた GCDAsyncSocket を使用しようとしています。GCDAsyncSocket は TCP レベルで動作するため、HTTP POST ヘッダーを自分で記述する必要があると考えています。そのため、漠然としか理解していない可動部分が多くあり、すべて間違っている可能性があります。しかし、ソケットが離陸することさえないようで、デバッグする方法さえわかりません。これが私の関連するコードです。明らかなエラーを見つけるかどうかを確認してください。
NSString *soapBody = ...; //Create the SOAP xml here with the part in the middle reserved for the Base64 encoded data (marked with string "CUT_HERE";
NSArray *soapBodyArray = [soapBody componentsSeparatedByString:@"CUT_HERE"];
self.p_soapBodyPart1 = [soapBodyArray[0] dataUsingEncoding:NSUTF8StringEncoding];
self.p_soapBodyPart2 = [soapBodyArray[1] dataUsingEncoding:NSUTF8StringEncoding];
socketQueue = dispatch_queue_create("socketQueue", NULL);//socketQueue is an ivar
self.p_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:socketQueue];//create the socket
NSError *err = nil;
if (![p_socket connectToHost:myURL onPort:80 error:&err]) // Asynchronous!
{
NSLog(@"I goofed: %@", err);
return;
}
NSString* httpHeader = [NSString stringWithFormat:@"POST %@ HTTP/1.1\r\nHost: %@\r\nAccept-Encoding: gzip, deflate\r\nContent-Type: text/xml\r\nAccept-Language: en-us\r\nAccept: */*\r\nSOAPAction: http://tempuri.org/myAction\r\nConnection: keep-alive\r\nUser-Agent: ...\r\n\r\n", webserviceOperationsPath, webserviceHost];//Create the HTTP POST header
[p_socket writeData:[httpHeader dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:1]; //enqueue the HTTP header
[p_socket writeData:self.p_soapBodyPart1 withTimeout:-1 tag:2]; //enqueue the first part of the SOAP xml
[self setUpStreamsForInputFile: [self.p_mediaURL path]];//set up NSInputStream to read from media file and encode it as Base64
ソケットは常に正常に接続されているようです。これは、このデリゲート メソッドを使用して確認できます。
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{
NSLog(@"Socket Connected");
}
setUpStreamsForInputFile メソッド (上記の最初のコード リストで呼び出されます):
- (void)setUpStreamsForInputFile:(NSString *)inpath {
self.p_iStream = [[NSInputStream alloc] initWithFileAtPath:inpath];
[p_iStream setDelegate:self];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, ^ {
[p_iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[p_iStream open];
[[NSRunLoop currentRunLoop] run];
});
}
ここで、前のメソッドの NSInputStream セットアップは、このデリゲートにイベントを送信します。
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
if (stream == self.p_iStream){
if(!self.p_tempMutableData) {
self.p_tempMutableData = [NSMutableData data];
}
uint8_t buf[24000];
unsigned int len = 0;
len = [p_iStream read:buf maxLength:24000];//read a chunk from the file
if(len) {
[p_tempMutableData appendBytes:(const void *)buf length:len];
NSString* base64encData = [Base64 encodeBase64WithData:self.p_tempMutableData];//encode the chunk
self.p_streamEncData = [base64encData dataUsingEncoding:NSUTF8StringEncoding];
[p_socket writeData:self.p_streamEncData withTimeout:-1 tag:3];//write the encoded chunk to the socket
}
}
break;
}
case NSStreamEventEndEncountered:
{
[stream close];
[stream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
stream = nil;
[p_socket writeData:self.p_soapBodyPart2 withTimeout:-1 tag:4];//write the second part of SOAP xml
break;
}
... //some other events handled here
}
}
ソケットは、このデリゲートを使用してログに出力することになっています
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
if (tag == 1)
NSLog(@"HTTP Header Written");
else if (tag == 2)
NSLog(@"Soap Part 1 written");
else if (tag == 3)
NSLog(@"File written");
else if (tag == 4)
NSLog(@"Soap Part 2 written");
}
しかし、これは一種のランダムに発生します。たとえば、最初の 2 つの if が呼び出される場合と呼び出されない場合があります。私がそうして、3番目の「if」(実際にエンコードされたデータを書き込んでいる場所)に到達すると、それは2〜3回しか書き込まれず、それだけです-ファイルのサイズを考えると、数が少なすぎると思います. SOAP xml の最後の部分を書き込む必要がある最後の「if」に到達することはありません。助けていただければ幸いです!前もって感謝します。
さらなる更新 (2013 年 3 月 19 日)
今日ソケットをテストすると、書き込みイベントがまったく発生しなくなりました。これは、ランダムであり、ひどく間違ったことをしていることを示しています。今日、接続は開きますが、次のデリゲート メソッドで確認できるように、ある時点でタイムアウトになります。
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{ // This method is executed on the socketQueue (not the main thread)
dispatch_async(dispatch_get_main_queue(), ^{
@autoreleasepool {
NSLog(@"socketDidDisconnect:withError: \"%@\"", err);
}
});
}
返す
socketDidDisconnect:withError: "Error Domain=NSPOSIXErrorDomain Code=60 "Operation timed out" UserInfo=0x1cd89b00 {NSLocalizedFailureReason=connect() 関数のエラー、NSLocalizedDescription=Operation timed out}"
上記のストリームデリゲートで Base64 データの書き込みをまだ実行している間。