Upload with Buffer iOS 7

I am trying to implement an Upload with random Data and measure the speed. For now i am generating my random NSData like this:

void * bytes = malloc("");
NSData * myData = [NSData dataWithBytes:bytes length:"bytes"];
free("bytes");

But there will be Memory Problems if i want to upload a big File...

My Upload process is like this:

NSURLSessionConfiguration *sessionConfig =
[NSURLSessionConfiguration defaultSessionConfiguration];

NSURLSession *session =
[NSURLSession sessionWithConfiguration:sessionConfig
                              delegate:self
                         delegateQueue:nil];

NSURL * urll = [NSURL URLWithString:UPLOAD_SERVER];
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:urll];
[urlRequest setHTTPMethod:@"POST"];
[urlRequest addValue:@"Keep-Alive" forHTTPHeaderField:@"Connection"];

NSString *boundary = @"*****";
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[urlRequest addValue:contentType forHTTPHeaderField: @"Content-Type"];

NSMutableData *body = [NSMutableData data];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// setting the body of the post to the reqeust
[urlRequest setHTTPBody:body];

void * bytes = malloc(250000000);
NSData * uploadData = [NSData dataWithBytes:bytes length:250000000];
free(bytes);

ulTask = [session uploadTaskWithRequest:urlRequest fromData:uploadData];

[ulTask resume];

Is there a way to upload with a buffer or something?! like generate small data, upload this and generate a new one and upload again?!

Answers


I would suggest just start an upload and just keep sending data. You can also avoid the creation of your 250mb buffer, by using uploadTaskWithStreamedRequest and then create an NSInputStream subclass that just keeps providing more data until you tell it to stop. You can implement URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: to monitor the progress of your upload (so you can presumably monitor the speed with which data is being sent).

Anyway, to create the upload request:

@interface ViewController () <NSURLSessionDelegate, NSURLSessionTaskDelegate>

@property (nonatomic, strong) CustomStream *inputStream;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.inputStream = [[CustomStream alloc] init];

    NSURL *url = [NSURL URLWithString:kURLString];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];

    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];

    NSURLSessionUploadTask *task = [session uploadTaskWithStreamedRequest:request];

    [task resume];

    // I don't know how you want to finish the upload, but I'm just going 
    // to stop it after 10 seconds

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.inputStream.finished = YES;
    });
}

You obviously have to implement the appropriate delegate methods:

#pragma mark - NSURLSessionTaskDelegate

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
    NSLog(@"%lld %lld %lld", bytesSent, totalBytesSent, totalBytesExpectedToSend);
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
{
    completionHandler(self.inputStream);
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"%s: error = %@; data = %@", __PRETTY_FUNCTION__, error, [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]);
}

#pragma mark - NSURLSessionDataDelegate

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
    self.responseData = [NSMutableData data];
    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    [self.responseData appendData:data];
}

And the CustomStream:

static NSInteger const kBufferSize = 32768;

@interface CustomStream : NSInputStream

@property (nonatomic, readonly) NSStreamStatus streamStatus;
@property (nonatomic, getter = isFinished) BOOL finished;

@end

@interface CustomStream ()

@property (nonatomic) NSStreamStatus streamStatus;
@property (nonatomic) void *buffer;

@end

@implementation CustomStream

- (instancetype)init
{
    self = [super init];
    if (self) {
        _buffer = malloc(kBufferSize);
        NSAssert(_buffer, @"Unable to create buffer");
        memset(_buffer, 0, kBufferSize);
    }
    return self;
}

- (void)dealloc
{
    if (_buffer) {
        free(_buffer);
        self.buffer = NULL;
    }
}

- (void)open
{
    self.streamStatus = NSStreamStatusOpen;
}

- (void)close
{
    self.streamStatus = NSStreamStatusClosed;
}

- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len
{
    if ([self isFinished]) {
        if (self.streamStatus == NSStreamStatusOpen) {
            self.streamStatus = NSStreamStatusAtEnd;
        }
        return 0;
    }

    NSUInteger bytesToCopy = MIN(len, kBufferSize);
    memcpy(buffer, _buffer, bytesToCopy);

    return bytesToCopy;
}

- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len
{
    return NO;
}

- (BOOL)hasBytesAvailable
{
    return self.streamStatus == NSStreamStatusOpen;
}

- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop
                  forMode:(__unused NSString *)mode
{}

- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop
                  forMode:(__unused NSString *)mode
{}

#pragma mark Undocumented CFReadStream Bridged Methods

- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop
                     forMode:(__unused CFStringRef)aMode
{}

- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop
                         forMode:(__unused CFStringRef)aMode
{}

- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags
                 callback:(__unused CFReadStreamClientCallBack)inCallback
                  context:(__unused CFStreamClientContext *)inContext {
    return NO;
}

@end

I'd suggest you refer to BJ Homer's article Subclassing NSInputStream for some of the background on a few of the cryptic methods in this NSInputStream subclass.


-(void) updateUserData:(NSDictionary*)data
     withImageToUpload:(NSData*)imageToUpload
               success: (void (^) (id responseObject))success
               failure: (void (^)(NSError* error))failure
{
    NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"PATCH"
                                                                                              URLString:@"URL_REQUEST/update_profile"
                                                                                             parameters:data
                                                                              constructingBodyWithBlock:^(id<AFMultipartFormData> formData)
                                    {
                                        [formData appendPartWithFileData:imageToUpload
                                                                    name:@"individual[avatar]"
                                                                fileName:@"avatar.jpg"
                                                                mimeType:@"image/jpeg"];
                                    }

                                                                                                  error:nil];

    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    NSProgress *progress = nil;

    NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request
                                                                       progress:&progress
                                                              completionHandler:^(NSURLResponse *response, id responseObject, NSError *error)
                                          {
                                              if (error)
                                              {
                                                  NSLog(@"Error: %@", error);
                                                  failure(error);
                                              } else
                                              {
                                                  [[NSNotificationCenter defaultCenter] postNotificationName:@"userDataDidUpdated" object:self];
                                                  success(responseObject);
                                              }
                                          }];

    [uploadTask resume];

}

Need Your Help

Authentication issue in BLE Bluetooth Low Energy device

android android-bluetooth bluetooth-lowenergy

We are making a IOT device with a BLE interface which uses the HM-11 (http://www.seeedstudio.com/wiki/Bluetooth_V4.0_HM-11_BLE_Module) breakout board hosting the chip CC2541 (http://www.ti.com/prod...