I have previously uploaded files to a presigned URL by doing the following:
NSData *data = [NSData dataWithContentsOfURL:self.videoURL];
[self.httpSessionManager.requestSerializer setValue:#"video/mp4" forHTTPHeaderField:#"Content-Type"];
[self.httpSessionManager PUT:operation.relativeURLString parameters:#{#"data": data} success:^(NSURLSessionDataTask *task, id responseObject) {
[self handleResponse:responseObject forSuccessfulOperation:operation];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
[self handleError:error forFailedOperation:operation];
}];
But the need to track uploading progress made me change this into:
NSData *data = [NSData dataWithContentsOfURL:self.videoURL];
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"PUT" URLString:operation.relativeURLString parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:data
name:#"data"
fileName:#"video.mp4"
mimeType:#"video/mp4"];
} error:nil];
NSURLSessionDataTask *uploadTask = [self.httpSessionManager uploadTaskWithStreamedRequest:request progress:progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (!error)
{
[self handleResponse:responseObject forSuccessfulOperation:operation];
}
else
{
[self handleError:error forFailedOperation:operation];
}
}];
[uploadTask resume];
This seems to upload the file successfully until I try to play it. It has the correct file size, but the file seems to be broken since it will not play. Am I misinterpreting how to use multipartFormRequest? I have come to understand that using NSStream or a memory mapped file instead of passing along NSData is preferable, but to my knowledge, this shouldn't be the cause of my issue, but a mere performance tweak.
Related
I'm using AFNetworking 3.0 and I try to send files to my server, my code work perfectly but it's very long to send the file especially when I try to send a video.
I compress the video quality and it's a little bit fast but to send a 5 sec video that take 15-20 sec.
I want to know if it's possible to improve the speed to send the file.
There is my code :
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:#"urlOfServer" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:videoData name:#"fileName" fileName:#"filename.mp4" mimeType:#"video/mp4"];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
//Update the progress view
NSLog(#"PERCENTAGE UPLOADED %f",uploadProgress.fractionCompleted);
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"%# %#", response, responseObject);
}
}];
[uploadTask resume];
Thank for your help
Since you only write to the debug console to report the progress and you don't change any UI elements, the dispatch_get_main_queue is not needed. Try to remove it and see if it makes a difference in performance.
I'm new to ios.
How to send an image or an video to php server. i have a key "Image"
for sending image and key "Video" to send video file. i need to send image or video with their keys, how can i send...?
Use Post method. And you should archive image/video to data and make sheet data to send it to server.
I suggest you use Alamofire to do this. This is the code.
let imageData = UIPNGRepresentation(image)!
Alamofire.upload(imageData, to: "https://httpbin.org/post").responseJSON { response in
debugPrint(response)
}
Here is the link.
Update
As you're familiar with OC, use AFNetworking instead. This is the code.
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:#"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:#"file://path/to/image.jpg"] name:#"file" fileName:#"filename.jpg" mimeType:#"image/jpeg" error:nil];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
// This is not called back on the main queue.
// You are responsible for dispatching to the main queue for UI updates
dispatch_async(dispatch_get_main_queue(), ^{
//Update the progress view
[progressView setProgress:uploadProgress.fractionCompleted];
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"%# %#", response, responseObject);
}
}];
[uploadTask resume];
Here is the link.
I'm working on an app in which I need to upload file to some server, and I'm using AFNetworking to upload file.
All work fine except if file size is short like 2mb and if more than this, server respond me error with status code 500 and doesn't upload file.
Here below is code how I am uploading.
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:lang forHTTPHeaderField:#"Accept-Language"];
[request setValue:authVal forHTTPHeaderField:#"authorization"];
[request setValue:[headerParams objectForKey:#"x-file-name"] forHTTPHeaderField:#"x-file-name"];
[request setValue:[headerParams objectForKey:#"x-convert-document"] forHTTPHeaderField:#"x-convert-document"];
[request setValue:[headerParams objectForKey:#"x-source"] forHTTPHeaderField:#"x-source"];
//convert parameters in to json data
if ([params isKindOfClass:[NSDictionary class]]) {
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params
options:NSJSONWritingPrettyPrinted
error:&error];
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
[request setURL:[NSURL URLWithString:action]];
[request setTimeoutInterval:200.0];
[request setHTTPMethod:#"POST"];
NSMutableData *postBody = [NSMutableData data];
[postBody appendData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:imageData];
NSLog(#"File size is : %.2f MB",(float)imageData.length/1024.0f/1024.0f);
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSInteger statusCode = [operation.response statusCode];
NSNumber *statusObject = [NSNumber numberWithInteger:statusCode];
successBlock(responseObject, statusObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}
The line of code [request setHTTPBody:imageData]; is setting NSData of image.
The same thing works on Android and its working fine but they'r using stream, first to write file into Physical path then by using stream to upload. Is this possible by using AFNetworking? or can I achieve this by using other approach too?
Looking for Suggestion.
Thanks
Note :- I have just implemented Image Upload service using AFNetworking 3.0,
-> Here kBaseURL means Base URL of server
->serviceName means Name of Service.
->dictParams means give parameters if needed, otherwise nil.
->image means pass your image.
Note :- This code written in NSObject Class and we have apply in our Project.
+ (void)requestPostUrlWithImage: (NSString *)serviceName parameters:(NSDictionary *)dictParams image:(UIImage *)image success:(void (^)(NSDictionary *responce))success failure:(void (^)(NSError *error))failure {
NSString *strService = [NSString stringWithFormat:#"%#%#",kBaseURL,serviceName];
[SVProgressHUD show];
NSData *fileData = image?UIImageJPEGRepresentation(image, 0.5):nil;
NSError *error;
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:strService parameters:dictParams constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
if(fileData){
[formData appendPartWithFileData:fileData
name:#"image"
fileName:#"img.jpeg"
mimeType:#"multipart/form-data"];
}
} error:&error];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager uploadTaskWithStreamedRequest:request progress:^(NSProgress * _Nonnull uploadProgress) {
NSLog(#"Wrote %f", uploadProgress.fractionCompleted);
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
[SVProgressHUD dismiss];
if (error)
{
failure(error);
}
else
{
NSLog(#"POST Response : %#",responseObject);
success(responseObject);
}
}];
[uploadTask resume];
}
--> Now apply in our project.
UIImage *imgProfile = //Pass your image.
[WebService requestPostUrlWithImage:#"saveImageWithData" parameters:nil image:imgProfile success:^(NSDictionary *responce) {
NSString *check = [NSString stringWithFormat:#"%#",[responce objectForKey:#"status"]];
if ([check isEqualToString: #"1"]) {
//Image Uploaded.
}
else
{
//Failed to Upload.
}
} failure:^(NSError *error) {
//Error
}];
Step 1: here I am creating the Request
NSMutableURLRequest *request1 = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST"
URLString:[NSString stringWithFormat:#"%#%#", API_MAIN_URL, IMAGE_UPLOAD]
parameters:param constructingBodyWithBlock:^(id formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:strImagePath]
name:#"sendimage"
fileName:[strImagePath lastPathComponent]
mimeType:#"image/png"
error:nil];
} error:nil];
[request1 setValue:authToken forHTTPHeaderField:#"Authorization"];
Step 2: here I am creating the Stream at given Location
[[AFHTTPRequestSerializer serializer] requestWithMultipartFormRequest:request1
writingStreamContentsToFile:[NSURL fileURLWithPath:[strImagePath stringByDeletingPathExtension]]
completionHandler:^(NSError *error){
Step 3: here I am creating uploading task.
///here is file
NSProgress *progress = nil;
NSURLSessionUploadTask *uploadTask = [self.manager uploadTaskWithRequest:request1
fromFile:[NSURL fileURLWithPath:strImagePath]
progress:&progress
completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
NSLog(#"response : %#\n\n responseObject : %#\n\n error : %#", response, responseObject, error);
}];
[uploadTask resume];
}
}];
}
My problem is before application going to background mode I want to write all the request HTTPBody(HTTPStream) at given location with using Step:1 and Step:2 and save all the request into the NSArray after writing all the HTTPStream in File(in application Foreground) mean while I'll show Image prepare to upload.
then I'll start creating background task with the help of Step3: with passing request into this which request I have stored into the NSArray.
with this approach I am not able to upload images.
But If I call all the steps together one by one then it will upload the image on the server, but with this approach my application should be in Foreground for create request because we need to write the HTTPBody at give location.
Please help me to come out this problem I am stuck on this from last 2 week.
My application needed more then 500 images uploading over the server.
try like this
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:#"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:#"file://path/to/image.jpg"] name:#"file" fileName:#"filename.jpg" mimeType:#"image/jpeg" error:nil];
} 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);
} else {
NSLog(#"%# %#", response, responseObject);
}
}];
[uploadTask resume];
I have done with this task, create all the upload task without resume them and save every task within the NSArray after creation.
Once all the task are created then call a method in which we resume all the tasks.
I can't understand why this is so hard. All the tutorials and articles online seem to be talking about the 1.0 api, which is pretty useless.
I've tried a few different ways and get different results. What am I doing wrong?
upload task - this seems to not be using a multipart form, wtf?
NSMutableURLRequest *request = [self.manager.requestSerializer multipartFormRequestWithMethod:#"POST"
URLString:[[NSURL URLWithString:url relativeToURL:[NSURL URLWithString:ApiBaseUrl]] absoluteString]
parameters:#{}
constructingBodyWithBlock:nil];
NSProgress *progress;
NSURLSessionUploadTask *task = [self.manager uploadTaskWithRequest:request
fromData:data
progress:&progress
completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(#"[error description] = %#", [error description]);
} else {
NSLog(#"success!");
}
}];
[task resume];
post with a block - this seems not to attach anything
[self.manager POST:url
parameters:#{}
constructingBodyWithBlock:^(id <AFMultipartFormData> formData) {
[formData appendPartWithFileData:data
name:#"post[picture]"
fileName:#"picture.jpg"
mimeType:#"image/jpeg"];
}
success:^(NSURLSessionDataTask *task, id response) {
NSLog(#"Success");
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"Error: %#", error);
}];
simple post - this seems to almost work...but not
[self.manager POST:url
parameters:#{#"post[picture][]":data}
success:^(NSURLSessionDataTask *task, id response) {
NSLog(#"Success");
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"Error: %#", error);
}];
I would love 1 to work, but I'm not sure why it doesn't.
For a properly formed "multipart/form-data" body, you need to use use the body construction block while creating the request. Otherwise the upload task is using the raw data as the body. For example, in your AFHTTPSessionManager subclass:
NSString *urlString = [[NSURL URLWithString:kPhotoUploadPath relativeToURL:self.baseURL] absoluteString];
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:#"POST" URLString:urlString parameters:params constructingBodyWithBlock:^(id <AFMultipartFormData> formData) {
[formData appendPartWithFileData:photo.data name:#"photo" fileName:#"photo.jpg" mimeType:#"image/jpeg"];
}];
NSURLSessionUploadTask *task = [self uploadTaskWithStreamedRequest:request progress:progress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) failure(error);
} else {
if (success) success(responseObject);
}
}];
[task resume];
Or, if you don't need to track upload progress, you can simply use:
[self POST:kPhotoUploadPath parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:photo.data name:#"photo" fileName:#"photo.jpg" mimeType:#"image/jpeg"];
} success:^(NSURLSessionDataTask *task, id responseObject) {
if (success) success(responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if (failure) failure(error);
}];
What Ray Lillywhite describes works perfectly fine (I would've made a comment on his post, but my reputation is too low).
Get the correct version of AFNetworking, containing this fix for updating progress when using multipart requests. At the moment of writing, that version is HEAD.
Create a NSMutableURLRequest with the help of multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:.
Build your form data with the help of one of the appendPartWith... methods.
Get a (upload) data task by calling the right uploadTaskWith... method. You NEED to use uploadTaskWithStreamedRequest:progress:completionHandler: if you want to use the NSProgress input parameter.