Upload photo from iPhone to 4D Server - ios

I am looking to upload a photo from iPhone to a 4D Server. Specifically to upload a photo taken on an iPhone, uploaded to a 4D Server and stored as a JPEG image in the WebFolder. I am using 4D Server version 12 and 13. I have looked at other postings here but I cannot make any of them apply to 4D. Anyone know how to do this?

It took me a while but I finally puzzled together the various procedures to make this work. Thumbs up to the iriphon blog (http://www.iriphon.com/2011/11/09/ios-uploading-an-image-from-your-iphone-to-a-server/) for putting me on the right track.
Here is the iOS code I used:
-(IBAction)uploadPhoto:(id)sender {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://www.domain.com/4DACTION/showcaseUploadPic"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60];
[request setHTTPMethod:#"POST"];
// We need to add a header field named Content-Type with a value that tells that it's a form and also add a boundary.
// I just picked a boundary by using one from a previous trace, you can just copy/paste from the traces.
NSString *boundary = #"----WebKitFormBoundaryQkZe6RUJZ2xbzXLB";
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#",boundary];
[request addValue:contentType forHTTPHeaderField: #"Content-Type"];
// end of what we've added to the header.
// the body of the post.
NSMutableData *body = [NSMutableData data];
// Now we need to append the different data 'segments'. We first start by adding the boundary.
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// Now append the image
// Note that the name of the form field is exactly the same as in the trace ('picBLOB' in my case).
// You can choose whatever filename you want.
[body appendData:[[NSString stringWithString:#"Content-Disposition: form-data; name=\"picBLOB\" filename=\"image001.jpg\"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
// We now need to tell the receiver what content type we have.
// In my case it's a jpg image. If you have a png, set it to 'image/png'.
[body appendData:[[NSString stringWithString:#"Content-Type: image/jpg\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
// Now we append the actual image data.
// I use a NSData object called photoData.
[body appendData:[NSData dataWithData:photoData]];
NSLog(#"photoData size = %i",[photoData length]);
// and again the delimiting boundary.
[body appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// adding the body we've created to the request.
[request setHTTPBody:body];
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"connection = %#",connection);
}
Don't forget to include the NSURLConnection delegates!
I used Wireshark, as described in the blog, to get the exact data I needed. I am using NSData to store the photo I had just taken with the camera and want to upload. Because of this my code is slightly different than if you wanted to upload a stored file.
Here is the 4D (version 12) code:
$ba:=BLOB size(picBLOB)
C_PICTURE(imageVar)
If (Undefined(picBLOB)) // in case user tries to reload /4DACTION/WEB_Set_Limits
C_BLOB(picBLOB)
End if
If (BLOB size(picBLOB)>0) // did user select a file for uploading?
C_STRING(255;$fileHeader)
$offset:=0 // BLOB to text requires a variable for the offset parameter
$fileHeader:=BLOB to text(picBLOB;Mac text without length;$offset;255)
$fileNameBegin:=Position("filename=";$fileHeader)+10
For ($i;$fileNameBegin;255)
If ($fileHeader≤$i≥=Char(Double quote))
$fileNameEnd:=$i
$i:=255
End if
End for
$fileName:=Substring($fileHeader;$fileNameBegin;$fileNameEnd-$fileNameBegin)
$contentTypeBegin:=Position("Content-Type:";$fileHeader)+14
For ($i;$contentTypeBegin;255)
If ($fileHeader≤$i≥=Char(Carriage return))
$contentTypeEnd:=$i
$i:=255
End if
End for
contentType:=Substring($fileHeader;$contentTypeBegin;$contentTypeEnd-$contentTypeBegin)
DELETE FROM BLOB(picBLOB;0;$contentTypeEnd+3)
BLOB TO PICTURE(picBLOB;imageVar;"JPEG")
$FILENAME:=":WebFolder:myPhoto.jpg"
WRITE PICTURE FILE($FILENAME;imageVar;"JPEG")
End if
Here is the 4D (version 13) code:
C_BLOB(picBLOB)
C_PICTURE(imageVar)
WEB GET BODY PART(1;picBLOB;myPhoto)
If (BLOB size(picBLOB)>0)
BLOB TO PICTURE(picBLOB;imageVar;"JPEG")
$FILENAME:=":WebFolder:myPhoto.jpg"
WRITE PICTURE FILE($FILENAME;imageVar;"JPEG")
End if

I am not particularly familiar with this "4D Server" but sounds like you would just need a simple server side PHP script to upload the photo, probably using NSData.
What exactly have you tried, or what is the extent of your knowledge in both of these areas?

This page has instructions for working with an earlier version. A more recent post on the iNug (4D's dev mailing list) suggests that this still works, perhaps with a few tweeks.
You'll get a better response asking on the iNug simply because the 4D community is so small.

Related

How to send the character "&" via HTTP POST to server with iOS 7?

I want to send some data via http post from my App to the server. All symbols can be accepted by the server, except the &.
the server is php;
content-type is application/x-www-form-urlencoded, and charset is utf-8.
Encoding is NSUTF8StringEncoding.
I tried also changing the & with URLEncoding, i.e. & --> %26. the server can receive it, but cannot display it recht. It's shown in %26, not &.
But, the server can properly received and displayed, if it is sent by android or web side.
The code is the following:
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"xxx.com/server.php"]];
postRequest.HTTPMethod = #"POST";
[postRequest setValue:#"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
NSString *bodyString = [NSString stringWithFormat:#"data=&&&&"];
postBody = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
NSURLConnection *postConnect = [[NSURLConnection alloc]initWithRequest:postRequest delegate:self];
hope this helps you
NSString *encodedString = [myString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
It won't replace your string inline; it'll return a new string. That's implied by the fact that the method starts with the word "string". It's a convenience method to instantiate a new instance of NSString based on the current NSString.
Note--that new string will be autorelease'd, so don't call release on it when you're done with it.
It should be with the function
(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)url, NULL, (CFStringRef)#"&", kCFStringEncodingUTF8))

Facing issues in uploading multiple documents Via single API developed in RUBY

I am trying to upload multiple documents (say: images and videos) at the same time with hitting on single API. The API is developed in Ruby ON Rails. I am trying to upload documents in iPhone using ASIHTTP methods. Here is the screenshot of getpostman. On getpostman, It is working fine. Multiple documents are working fine but when I am using same API in xcode then its giving me error.
What I have done till now is, I have used two ways.
1.) Sending multiple documents's data using dictionaries. Here is the code snippet:-
[self setRequest:[ASIFormDataRequest requestWithURL:[NSURL URLWithString:#"http://onepgr.com/docs/create6_api"]]];
[request setPostValue:#"11" forKey:#"clientname"];
[request setPostValue:#"1" forKey:#"onepgr_apicall"];
[request setPostValue:pageID forKey:#"page_id"];
//[request setPostValue:#"true" forKey:#"multiple"];
//[request setPostValue:array forKey:#"doc"];
[request setPostValue:#"" forKey:#"passcode"];
[request setPostValue:#"33" forKey:#"clientappkey"];
[request setPostValue:#"22" forKey:#"clientappid"];
[request setTimeOutSeconds:20];
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
[request setShouldContinueWhenAppEntersBackground:YES];
#endif
[request setUploadProgressDelegate:progressView];
[request setDelegate:self];
[request setDidFailSelector:#selector(uploadFailed:)];
[request setDidFinishSelector:#selector(uploadFinished:)];
//Uncomment For Multiple Upload
for (int i = 0; i<self.chosenImages.count; i++)
{
UIImage *img = [self.chosenImages objectAtIndex:i];
NSString *str = [NSString stringWithFormat:#"Image_File_%d.png",i+1];
strFileName = str;
strContentType = #"image/png";
fileData = UIImageJPEGRepresentation(img, 1.0);
if (i == 0)
[request setData:fileData withFileName:strFileName andContentType:strContentType forKey:#"doc"];
else
[request addData:fileData withFileName:strFileName andContentType:strContentType forKey:#"doc"];
}
2.) I have tried sending the data in for loop as well. But nothing worked for me.
Looking forward to have some answers that can help me out.
Thanks,
Nikhil
Use http://www.charlesproxy.com/ to check the differences between the 2 requests
My personal bet is that the problem is that you are setting the image data within the loop always to the same key.

HTTP post w/ JSON to rails server from iOS

I have succeeded in making a post using a HTTP Client by setting the content type as application/json and this json code:
{
"order": {
"name": "Tia Carter",
"location": "Corams",
"phone_number": "707",
"food": "Bobcat Burger"
}
}
The code works perfect and the order is registered in the database. I am trying to work this into my iOS app but keep getting syntax errors regarding the colons in the json. This is the objective-c code:
NSURL *nsURL = [[NSURL alloc] initWithString:#"http://0.0.0.0:3000/orders.json"];
NSMutableURLRequest *nsMutableURLRequest = [[NSMutableURLRequest alloc] initWithURL:nsURL];
// Set the request's content type to application/x-www-form-urlencoded
[nsMutableURLRequest setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
// Set HTTP method to POST
[nsMutableURLRequest setHTTPMethod:#"POST"];
// Set up the parameters to send.
NSString *paramDataString = [NSString stringWithFormat:#
"{ "order": {"%#:" ="%#","%#:"="%#","%#:"="%#","%#:"="%#"}}", #"name", _name.text, #"location", _location.text, #"phone_number", _phoneNumber.text, #"food", _order.text];
// Encode the parameters to default for NSMutableURLRequest.
NSData *paramData = [paramDataString dataUsingEncoding:NSUTF8StringEncoding];
// Set the NSMutableURLRequest body data.
[nsMutableURLRequest setHTTPBody: paramData];
// Create NSURLConnection and start the request.
NSURLConnection *nsUrlConnection=[[NSURLConnection alloc]initWithRequest:nsMutableURLRequest delegate:self];
I'd appreciate any ideas or guidance. Thanks.
I believe you have two problems:
You didn't escape quotas (put \ before all of them)
You don't need to put text "name", "location" and etc in parameters (it's not a problem per se, just a style thing)
Also, I would recommend to work with NSDictionary and convert it to JSON when you need to (it will save you a lot of nerves for unescaped quotas, missing bracket and so on).
Look this question how to convert NSDictionary to JSON:
Generate JSON string from NSDictionary in iOS

iOS - POSTing file upload requests to Copy.com

Working with the new Copy.com API. Successfully signed in with OAuth1 and have made requests for profile data, and can successfully create folders.
If you look at the API docs here: https://www.copy.com/developer/documentation#api-calls/filesystem and go to the section on making POST requests to the API to create new files, the instructions for the headers are a little strange to me. See here:
Content-Type:multipart/form-data; boundary=----WebKitFormBoundary5dcD4Bk7SevSsaMg
Content-Disposition: form-data; name="X-Api-Version"
1.0
------WebKitFormBoundary5dcD4Bk7SevSsaMg
Content-Disposition: form-data; name="file"; filename="animation.gif"
Content-Type: image/gif
<BASE64 ENCODED FILE STRUCTURE>
------WebKitFormBoundary5dcD4Bk7SevSsaMg--
As you can see, there are multiple header fields for Content-Type and Content-Disposition, separated by the boundary parameter. When using NSMutableURLRequest and -setValue:forHTTPHeaderField I just overwrite previous values. Here's what I have for my header fields:
NSString *boundaryString = #"----WebKitFormBoundary5dcD4Bk7SevSsaMg";
NSString *disposition = #"Content-Disposition: form-data; name=\"ThankYou.mp3\" filename=\"ThankYou.mp3\"";
NSString *type = #"Content-Type: audio/mp3";
NSData *dispositionData = [disposition dataUsingEncoding:NSUTF8StringEncoding];
NSData *typeData = [type dataUsingEncoding:NSUTF8StringEncoding];
NSData *boundaryData = [boundaryString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *songData = [NSMutableData data];
[songData appendData:boundaryData];
[songData appendData:dispositionData];
[songData appendData:typeData];
[songData appendData:mp3Data]; //initiated before. From my mainBundle.
[songData appendData:boundaryData];
[request setHTTPBody:songData];
[self.myAuth authorizeRequest:request];
[request setValue:#"1" forHTTPHeaderField:#"X-Api-Version"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"multipart/form-data; boundary=----WebKitFormBoundary5dcD4Bk7SevSsaMg" forHTTPHeaderField:#"Content-Type"];
[request setValue:#"form-data; name=\"X-Api-Version\"=1.0" forHTTPHeaderField:#"Content-Disposition"];
As you can see, I tried to replicate the structure of the POST request by appending a boundary, Content-Disposition and Content-Type in front of the actual Body Data, and then append a boundary at the end.
At the end of this I am only created a folder called "ThankYou.mp3" on Copy.com, which is what happens when you make a POST request with empty body data. I assure you the body data is not empty :-)
Can anyone help me out here?
The code used in this class is tested to work: COCopyClient.m
It might be easier to just use the library: copy-mac-ios-sdk

Large Base64 uploads in iOS

Description:
I need to send a base 64 encoded string in a HTTP POST parameter that represents a file to be uploaded.
Current method: I'm using a ASIFormDataRequest and encoding my file into a base64 string like so:
NSData *data = [[NSFileManager defaultManager] contentsAtPath:#"my path here"];
NSString *base64Data = [data encodeBase64ForData];
However when uploading large files the App runs out of memory and dies a horrid death!
Proposed solution: Does anybody now how I would go about, say, reading the file from disk, converting it to base64 strings in chunks and attaching the converted base64 string to a parameter in the HTTP request?
Effectively any way that avoids reading the whole file into memory.
Any input would be greatly appreciated!
My http request code in full:
NSURL *url = [NSURL URLWithString:requestProgressURLString];
NSData *data = [[NSFileManager defaultManager] contentsAtPath:evidence.uri];
NSString *base64Data = [data encodeBase64ForData];
NSURL *fileURL = [[NSURL alloc] initWithString:evidence.uri];
request = [ASIFormDataRequest requestWithURL:url];
request.shouldAttemptPersistentConnection = false;
[request setShowAccurateProgress:YES];
[request setPostValue:accessToken forKey:#"AccessToken"];
[request setPostValue:[[fileURL path] lastPathComponent] forKey:#"Filename"];
[request setPostValue:evidence.name forKey:#"Title"];
[request setPostValue:base64Data forKey:#"FileData"]; //wants string value (base64 encoded)
[request addRequestHeader:#"Content-Type" value:#"application/x-www-form-urlencoded"];
[request setTimeOutSeconds:60];
[request setUploadProgressDelegate:self];
[request setDownloadProgressDelegate:self];
[request setDelegate:self];
[request setDidFinishSelector:#selector(requestDone:)];
[request setDidFailSelector:#selector(requestWentWrong:)];
I would split this problem into two:
1. Encode large file to Base64 in chunks
2. Upload that file
1. Encode large file to Base64 in chunks:
Find some code that encodes NSData instances to Base64. Then use one NSInputStream to read data in chunks, encode it and use NSOutputStream to write to a temporary file. I found this question/answer: Is it possible to base64-encode a file in chunks? Make the chunk size a multiple of 3 and it should work. You may want to move this to background thread.
Important: This will create a temporary file, that is larger than the original. There must be enough space on device for that moment. After you begin uploading you can delete it.
2. Upload that file
It seems that you have this already done. You can use that ASIFormDataRequest exactly like in your example. And since it makes a private copy of the file (it builds the multipart POST file), you can delete your copy.
If you want to optimize it by parallelizing these steps, you will need to find another way to upload the multipart file.
Instead of reading the file in one hit you need to read it in chunks
You can use an input stream to do this
Apple have some info on here
Basically you need to create an NSInputStream pointed at your file as in the example
- (void)setUpStreamForFile:(NSString *)path {
// iStream is NSInputStream instance variable
iStream = [[NSInputStream alloc] initWithFileAtPath:path];
[iStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[iStream open];
}
You can then read the data in chunks using the NSStreamDelegate method
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
Again Apple give an example of this
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
if(!_data) {
_data = [[NSMutableData data] retain];
}
uint8_t buf[1024];
unsigned int len = 0;
len = [(NSInputStream *)stream read:buf maxLength:1024];
if(len) {
[_data appendBytes:(const void *)buf length:len];
// bytesRead is an instance variable of type NSNumber.
[bytesRead setIntValue:[bytesRead intValue]+len];
// DO SOMETHING with DATA HERE
} else {
NSLog(#"no buffer!");
}
break;
}
The size of chunks you choose will need to be a balance of memory performance vs network performance as there is obviously some overhead with the HTTP headers
You can also further optimise by sending your NSData directly ( and therefore save the base64 overhead) check this post
File Upload to HTTP server in iphone programming

Resources