jjzjj

python - iOS分块上传

coder 2023-08-21 原文

我正在尝试将用户地址簿中的联系人流式传输到我们的服务器。将所有联系人一次拉入内存可能会崩溃或使设备无响应。我不想承担将所有联系人写入文件和上传文件的开销。我可以看到通过网络发送的数据,但看起来格式无效。服务器无法识别请求正文。

我正在从地址簿中读取联系人并将它们写入 NSOutputStream。此 NSOutputStream 通过此代码与 NSInputStream 共享一个缓冲区

Buffering NSOutputStream used as NSInputStream?

//
//  NSStream+BoundPairAdditions.m
//  WAControls
//
//

#import "NSStream+BoundPairAdditions.h"
#include <sys/socket.h>

static void CFStreamCreateBoundPairCompat(
                                          CFAllocatorRef      alloc,
                                          CFReadStreamRef *   readStreamPtr,
                                          CFWriteStreamRef *  writeStreamPtr,
                                          CFIndex             transferBufferSize
                                          )
// This is a drop-in replacement for CFStreamCreateBoundPair that is necessary because that
// code is broken on iOS versions prior to iOS 5.0 <rdar://problem/7027394> <rdar://problem/7027406>.
// This emulates a bound pair by creating a pair of UNIX domain sockets and wrapper each end in a
// CFSocketStream.  This won't give great performance, but it doesn't crash!
{
#pragma unused(transferBufferSize)
    int                 err;
    Boolean             success;
    CFReadStreamRef     readStream;
    CFWriteStreamRef    writeStream;
    int                 fds[2];
    
    assert(readStreamPtr != NULL);
    assert(writeStreamPtr != NULL);
    
    readStream = NULL;
    writeStream = NULL;
    
    // Create the UNIX domain socket pair.
    
    err = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
    if (err == 0) {
        CFStreamCreatePairWithSocket(alloc, fds[0], &readStream,  NULL);
        CFStreamCreatePairWithSocket(alloc, fds[1], NULL, &writeStream);
        
        // If we failed to create one of the streams, ignore them both.
        
        if ( (readStream == NULL) || (writeStream == NULL) ) {
            if (readStream != NULL) {
                CFRelease(readStream);
                readStream = NULL;
            }
            if (writeStream != NULL) {
                CFRelease(writeStream);
                writeStream = NULL;
            }
        }
        assert( (readStream == NULL) == (writeStream == NULL) );
        
        // Make sure that the sockets get closed (by us in the case of an error,
        // or by the stream if we managed to create them successfull).
        
        if (readStream == NULL) {
            err = close(fds[0]);
            assert(err == 0);
            err = close(fds[1]);
            assert(err == 0);
        } else {
            success = CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
            assert(success);
            success = CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
            assert(success);
        }
    }
    
    *readStreamPtr = readStream;
    *writeStreamPtr = writeStream;
}

// A category on NSStream that provides a nice, Objective-C friendly way to create
// bound pairs of streams.

@implementation NSStream (BoundPairAdditions)

+ (void)createBoundInputStream:(NSInputStream **)inputStreamPtr outputStream:(NSOutputStream **)outputStreamPtr bufferSize:(NSUInteger)bufferSize
{
    CFReadStreamRef     readStream;
    CFWriteStreamRef    writeStream;
    
    assert( (inputStreamPtr != NULL) || (outputStreamPtr != NULL) );
    
    readStream = NULL;
    writeStream = NULL;
    
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
#error If you support Mac OS X prior to 10.7, you must re-enable CFStreamCreateBoundPairCompat.
#endif
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && (__IPHONE_OS_VERSION_MIN_REQUIRED < 50000)
#error If you support iOS prior to 5.0, you must re-enable CFStreamCreateBoundPairCompat.
#endif
    
    if (NO) {
        CFStreamCreateBoundPairCompat(
                                      NULL,
                                      ((inputStreamPtr  != nil) ? &readStream : NULL),
                                      ((outputStreamPtr != nil) ? &writeStream : NULL),
                                      (CFIndex) bufferSize
                                      );
    } else {
        CFStreamCreateBoundPair(
                                NULL,
                                ((inputStreamPtr  != nil) ? &readStream : NULL),
                                ((outputStreamPtr != nil) ? &writeStream : NULL), 
                                (CFIndex) bufferSize
                                );
    }
    
    if (inputStreamPtr != NULL) {
        *inputStreamPtr  = CFBridgingRelease(readStream);
    }
    if (outputStreamPtr != NULL) {
        *outputStreamPtr = CFBridgingRelease(writeStream);
    }
}

@end

在这里,我通过处理 NSOutputStream 委托(delegate)来构建请求主体。

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
    
    switch(eventCode) {
        case NSStreamEventHasSpaceAvailable: {
            
            if(self.contactIndex == 0 && [self.producerStream hasSpaceAvailable]) {
                 NSMutableData *data = [[NSMutableData alloc] init];
                [data appendData:[@"\r\n\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
                [data appendData:[@"{\"contacts\": [" dataUsingEncoding:NSUTF8StringEncoding]];
                [self.producerStream write:[data bytes] maxLength:[data length]];
            }
            
            while([self.producerStream hasSpaceAvailable] &&  self.contactIndex < [self.dataContactIDs count]) {
                NSMutableData *contactData = [[[self getNextContact] dataUsingEncoding:NSUTF8StringEncoding] mutableCopy];
                if(self.contactIndex < [self.dataContactIDs count]) {
                    [contactData appendData:[@"," dataUsingEncoding:NSUTF8StringEncoding]];
                }
                
                [self.producerStream write:[contactData bytes] maxLength:[contactData length]];
            }
            
            if(self.contactIndex == self.dataContactIDs.count) {
                 NSMutableData *data = [[NSMutableData alloc] init];
                [data appendData:[@"]}" dataUsingEncoding:NSUTF8StringEncoding]];
                [data appendData:[@"\r\n\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
                [self.producerStream write:[data bytes] maxLength:[data length]];
               
                [stream close];
                [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
                stream = nil;
            }
        } break;
        case NSStreamEventHasBytesAvailable: {
        } break;
        case NSStreamEventErrorOccurred: {
        } break;
        case NSStreamEventEndEncountered: {
            [stream close];
            [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            stream = nil;
        } break;
        default: {
        } break;
    }
}

我正在使用 AFNetworking 进行联网。我将请求主体流设置为 NSInputStream。

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:@"POST"];
[request setValue:@"application/json; charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBodyStream:inputStream];

AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFHTTPResponseSerializer serializer];

[op setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
    NSLog(@"PROGRESS %d %lld %lld", bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}];

[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    [self processResponse:responseObject success:success error:error log:log];
 } failure:^(AFHTTPRequestOperation *operation, NSError *e) {
     [self processError:e op:operation error:error log:log];
 }];


[[NSOperationQueue mainQueue] addOperation:op];

然后网络请求是这样的:(使用 Wireshark 捕获)

POST /upload?token=dd224bceb02929b36d35&agent=iPhone%20Simulator&v=1.0 HTTP/1.1
Host: localhost:6547
Transfer-Encoding: Chunked
Accept-Encoding: gzip, deflate
Content-Type: application/json; charset=UTF-8
Accept-Language: en-us
Connection: keep-alive
Accept: */*
User-Agent: MyApp/2.0 CFNetwork/672.0.8 Darwin/13.0.0

9BD



{"contacts": [(valid json array)]}



0

我不确定为什么请求正文中包含 9BD 和 0。我认为缓冲区的设置方式存在错误,我相信这会导致服务器忽略 http 正文,因为它无效。看起来我正在正确构建请求吗?有一个更好的方法吗?我正在使用 pyramid/python 来处理请求。服务器收到请求没问题,但是请求体是空的。

编辑

如果我不发送任何联系人,“9BD”就会消失。如果我更改联系人数据,“9BD”会更改为不同的字符。 “0”总是在底部。

编辑2

Jim 指出请求的格式有效。这意味着服务器没有正确处理流。请求可以正常访问服务器,服务器也可以正常回复。但是,我没有看到任何请求正文。服务器正在运行 Pyramid/python。在服务器上,request.body 是空的。

最佳答案

这个要求很好。您的请求已分 block :

Transfer-Encoding: Chunked

9BD 表示下一个 block 的长度。末尾的零表示没有更多的 block 。

参见 section 3.6.1 of RFC 2616了解详情。

您的问题可能是您的服务器不理解分 block 请求。

关于python - iOS分块上传,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20107529/

有关python - iOS分块上传的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  3. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  4. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  5. ruby-on-rails - 添加回形针新样式不影响旧上传的图像 - 2

    我有带有Logo图像的公司模型has_attached_file:logo我用他们的Logo创建了许多公司。现在,我需要添加新样式has_attached_file:logo,:styles=>{:small=>"30x15>",:medium=>"155x85>"}我是否应该重新上传所有旧数据以重新生成新样式?我不这么认为……或者有什么rake任务可以重新生成样式吗? 最佳答案 参见Thumbnail-Generation.如果rake任务不适合你,你应该能够在控制台中使用一个片段来调用重新处理!关于相关公司

  6. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  7. ruby-on-rails - 有没有办法为 CarrierWave/Fog 设置上传进度指示器? - 2

    我在Rails应用程序中使用CarrierWave/Fog将视频上传到AmazonS3。有没有办法判断上传的进度,让我可以显示上传进度如何? 最佳答案 CarrierWave和Fog本身没有这种功能;你需要一个前端uploader来显示进度。当我不得不解决这个问题时,我使用了jQueryfileupload因为我的堆栈中已经有jQuery。甚至还有apostonCarrierWaveintegration因此您只需按照那里的说明操作即可获得适用于您的应用的进度条。 关于ruby-on-r

  8. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  9. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  10. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

随机推荐