I want to upload video using PUT request and this is my experience.
I was trying many possibilities of doing PUT request using AFNetworking 2.0, but everything fails.
But I resolved this and this "Question" is information for everybody else who is asking solution.
This works for me:
NSDictionary *headersDict = #{#"Content-Length": [NSString stringWithFormat:#"%#", mySize], #"Content-Type": #"video/mp4", #"Accept": #"application/json", #"Authorization": [NSString stringWithFormat:#"Basic %#", hash]};
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlToPut];
[request setHTTPMethod:#"PUT"];
[request setHTTPBody:videoData];
[request setAllHTTPHeaderFields:headersDict];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// if everything run great, we have to invalidate timer to notify
[uploadTimer invalidate];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", error);
}];
// start timer where app will be checking upload progres on the server
uploadTimer = [NSTimer scheduledTimerWithTimeInterval:.1
target:self
selector:#selector(checkUploadProgressForRecord:)
userInfo:record
repeats:YES];
[operation start];
Anybody has another solution?
You approach has the following issues:
The PUT method is not the suggested method for uploading a file. PUT can be used, but this requires that the server MUST create the resource at the specified URI. In most cases however, the server will create a new URI (which is unknown to the client) when saving the uploaded file locally on the server. In this case, a POST method must be used.
You are using a NSData object which represents the content of the file and assign it the request with setHTTPBody. This is problematic regarding system memory when the file is large and the NSData object uses a heap allocated buffer to hold the file's content. You should either use a NSData object which uses a memory mapped file or a NSStream to represent the file content.
You are too vague about the request headers:
`[request setAllHTTPHeaderFields:headersDict];`
Request header are important. You should set the Content-Type and possible the Content-Length header.
For "Form-based File Upload", see also RFC 1867.
Related
I have a problem when users use my app and they lost connection or use airplane mode.
My app server side doesn't set any cache policy and for the time being I can't change it. I migrated from AFNetworking 1.x to 2.0 and now I'm using AFHTTPRequestOperationManager when making requests. The problem is that because I have no cache policy on the server side, every request is made to the server (which for now is fine) but if the user is not able to connect to my server, it doesn't load the cached request.
So, I'm trying the following, using AFHTTPRequestOperation directly like this:
NSURL *URL = [NSURL URLWithString:filePath];
NSMutableURLRequest *request = [[NSURLRequest requestWithURL:URL] mutableCopy];
if (![[AFNetworkReachabilityManager sharedManager] isReachable]) {
[request setCachePolicy:NSURLRequestReturnCacheDataElseLoad];
}
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[[NSOperationQueue mainQueue] addOperation:op];
This way, if AFNetworkReachabilityManager tells me there's no connectivity, I configured the cache policy for the request and it is loaded from the cache correctly.
The question is, is this the right approach to this situation?
You just need to subclass the AFHTTPSessionManager and check if the client is offline. Then you can change the cache policy or force the app to use cached data.
I have seen a lot of major changes with the RestKit framework in version 0.20.x for the iOS platform.
The one thing I haven't found so far on the web is an example of how to download a binary file with the new version of RestKit.
I need to send a JSON object to a REST service and expect a binary file in return. Would seem simple, wouldn't it but for some reason RestKit only expects JSON (and the common internet content types such as XML) to come back.
The JSON object essentially is a request object telling the service which image it should go and get for me.
Fortunately I have managed to use the underlying AFNNetworking framework to help me with this and leverage the RestKit serializer to produce the request object I needed.
MyRequestClass *request = // ... get my request class instance
RKObjectManager *manager = [RKObjectManager sharedManager];
NSMutableURLRequest *downloadRequest = [manager requestWithObject:request method:RKRequestMethodPOST path:ROUTE_URL_MY_SERVICE parameters:nil];
AFHTTPRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:downloadRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// Use my success callback with the binary data and MIME type string
callback(operation.responseData, operation.response.MIMEType, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Error callback
callback(nil, nil, error);
}];
[manager.HTTPClient enqueueHTTPRequestOperation:requestOperation];
I am developing an iOS app that uses the Evernote API. Everything has been working fine, but then I started getting "code 403" to my requests.
Authentication to the service goes well: I am able log on and download all info I need (Notebooks, Notes, Note content, etc...). But when I try to get the thumbnails, I get 403.
My code for the request:
NSString *thumbnailPath = [NSString stringWithFormat:#"%#thm/note/%#?75", [[EvernoteSession sharedSession] webApiUrlPrefix], note.guid];
NSLog(#"THUMBNAILPATH %#", thumbnailPath);
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:[[EvernoteSession sharedSession] webApiUrlPrefix]]];
[httpClient clearAuthorizationHeader];
[httpClient setAuthorizationHeaderWithToken:[[EvernoteSession sharedSession] authenticationToken]];
[httpClient postPath:thumbnailPath parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
note.thumbnail = responseObject;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"REQUEST: %#", thumbnailPath);
NSLog(#"Error: %#", error.localizedDescription);
}];
If I copy what the "REQUEST:" log result, it is a well-formatted link that gives me the thumbnail in my browser. But the second log gives me: "Error: Expected status code in (200-299), got 403".
I am out of ideas. Can anyone help?
You are not passing in the auth token correctly. This is how you would request a thumbnail for a note on iOS :
- (void)getThumbnailWithNoteGuid:(NSString*)noteGUID {
EvernoteSession* session = [EvernoteSession sharedSession];
NSString* fullTumbnailURL = [NSString stringWithFormat:#"%#thm/note/%#",[[EvernoteSession sharedSession]webApiUrlPrefix],noteGUID];
NSURL *url = [NSURL URLWithString:fullTumbnailURL];
NSMutableURLRequest* urlReq = [NSMutableURLRequest requestWithURL:url];
[urlReq setHTTPMethod:#"POST"];
[urlReq setHTTPBody:[[NSString stringWithFormat:#"auth=%#",[session.authenticationToken URLEncodedString]] dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"full URL %#",fullTumbnailURL);
[NSURLConnection sendAsynchronousRequest:urlReq queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *urlREsp, NSData *respData, NSError *error) {
if(error == nil) {
NSLog(#"Thumbail data : %#",respData);
};
}];
}
403 - means forbidden. I haven't got expirience working with evernote but in based on other SDK's you're doing something wrong with request either you should go to evernote developers page. log in to your's account and look for some triggers. may be there some triggers which you should turn on to use this features
You should:
Make sure that you're hitting the fully constructed URL you intend to
Examine the entire returned body to see if there is additional error info provided
Examine full URL
Set a breakpoint in -[AFHTTPClient postPath:
parameters:
success:
failure:]. In the debugger, type po request to see the full URL that AFNetworking is hitting. Make sure that's what you want.
Examine entire body
In your failure block, you're only looking at the error object, created by AFNetworking to summarize the issue. However, the Evernote API could be providing additional info in the response body, which you can look at with NSLog(#"Response Body: %#", [operation responseString]).
Summary
Ultimately, your AFNetworking code is fine - this looks like an Evernote API issue. You're making the request wrong, your token is expired, or there's a bug on their end.
Side notes
It's inefficient to create a new AFHTTPClient instance for every request. You should probably use the singleton pattern to have one that sticks around for the lifetime of your app.
You should do some error checking before note.thumbnail = responseObject;. responseObject could be anything; make sure it's what you expect before you call your setter.
So I am trying to upload a file (picture) to the a server using their web service called UploadFile which takes 2 variables.
FileInfo info and a int requestId (which in my case will always be 0)
The FileInfo object contains several variables name (String), description(String), content(binary data of the file, in this case it would be the image), id (String), and name (String)
How do I interface with this server to make the request go through? Normally when I have been pulling/posting information I have just been doing JSON calls, but I am guessing uploading is different. I am not sure how to do this when the service requires a custom object to be passed.
Do I need to create the object in my App?
I am trying to use AFNetworking's AFHTTPRequestOperations for this.
I am trying to use their example as a springboard, but I still need to make sure on what to change in their upload example since the example uses direct uploading of an image and I need to upload a FileInfo object instead of a jpeg.
NSURL *url = [NSURL URLWithString:#"https://SomeDomain.com/Services/FileService.svc/UploadFile"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSData *imageData = UIImageJPEGRepresentation([self.photoImageView image], 0.5);
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:#"POST" path:#"/upload" parameters:nil constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData:imageData name:#"avatar" fileName:#"Upload.jpg" mimeType:#"image/jpg"];
}];
NSLog(#"Request %#", [request description]);
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSLog(#"Operation: %#", [operation description]);
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
NSLog(#"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"RESPONSE: %#", [responseObject description]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failed: %#", [error description]);
}];
[httpClient enqueueHTTPRequestOperation:operation];
Thanks,
Alan
if you see the AFNetworking documentation of the methode multipartFormRequestWithMethod...you have a Dictionnary parameter where you can putt all the infos needed by the Web service.
/** Creates an NSMutableURLRequest object with the specified HTTP
method and path, and constructs a multipart/form-data HTTP body,
using the specified parameters and multipart form data block. See
http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2
Multipart form requests are automatically streamed, reading files
directly from disk along with in-memory data in a single HTTP body.
The resulting NSMutableURLRequest object has an HTTPBodyStream
property, so refrain from setting HTTPBodyStream or HTTPBody on
this request object, as it will clear out the multipart form body
stream. #param method The HTTP method for the request. This
parameter must not be GET or HEAD, or nil. #param path The path
to be appended to the HTTP client's base URL and used as the request
URL. #param parameters The parameters to be encoded and set in the
request HTTP body. #param block A block that takes a single argument
and appends data to the HTTP body. The block argument is an object
adopting the AFMultipartFormData protocol. This can be used to
upload files, encode HTTP body as JSON or XML, or specify multiple
values for the same parameter, as one might for array values.
#return An NSMutableURLRequest object */
Refer this. They have given different methods:
https://github.com/AFNetworking/AFNetworking
So all I'm trying to do is serve up a .json file somewhere (I've tried both my own personal server, and also on AppFog), then performing a GET request using AFJSONRequestOperation on the iOS platform. My code to do this request is as follows:
AFHTTPClient *aclient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:kBaseURL]];
NSURLRequest *request = [aclient requestWithMethod:#"GET"
path:#"voucher.json"
parameters:nil];
AFJSONRequestOperation *op = [[AFJSONRequestOperation alloc] initWithRequest:request];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"%#", (NSString *)responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[op start];
The problem here is that say the .json file initially contained an array of 2 strings. The above code would fetch these objects just fine. Then if I edited the .json file with another 3 strings, rerunning the code would continue to fetch only the 2 old strings.
Only 10-15 minutes after I made the change to the .json file will a refresh give me the updated data. At first I thought this was a caching issue, but setting [client setCachingPolicy:] didn't make any difference.
This issue is present whether I host my .json file on my static server, or running in a Node express server on AppFog. It just keeps returning an old version of the resource requested.
I'm really puzzled about this: so any help would be greatly appreciated!
Make sure the url request is not cacheing the data. I had to implement the following so I would get new json data each time a fetch was made.
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:5];
This is likely a caching issue - you could change the cache policy on the request or append something onto the query string, like the current time.
To do this you could use something like:
[aclient requestWithMethod:#"GET"
path:[NSString stringWithFormat:#"voucher.json?%#", [NSDate date]]
parameters:nil];