How to download a FaceBook user photo - c++

I use to use CCHttpRequest(cocos2dx) to request a photo from internet and it works for URL like "https://www.baidu.com/img/bdlogo.png".
However I can't download the FB friend photo by CCHttpRequest. The photo URL is "https://graph.facebook.com/1386570461651640/picture…"
Furthermore I tried NSRequest(OC). It worked for this URL and downloaded the photo.
So, anyone knows how to download friends photo by CCHttpRequest or any other method which works in *.cpp file.

This code
string _id = "" // id require to whome you want to fectch photo
HttpRequest* request = new (std::nothrow) HttpRequest();
string url = "https://graph.facebook.com/"+_id+"/picture?height=120
&width=120";
request->setUrl(url.c_str());
request->setRequestType(cocos2d::network::HttpRequest::Type::GET);
request->setResponseCallback(CC_CALLBACK_2(HellowWorld::onRequestImgCompleted, this));
request->setTag("GetImage");
HttpClient::getInstance()->send(request);
request->release();
void HellowWorld::onRequestImgCompleted(HttpClient *sender, HttpResponse *response)
{
log("AppDelegate::onHttpRequestCompleted - onHttpRequestCompleted BEGIN");
if (!response)
{
log("onHttpRequestCompleted - No Response");
return;
}
log("onHttpRequestCompleted - Response code: %lu", response->getResponseCode());
if (!response->isSucceed())
{
log("onHttpRequestCompleted - Response failed");
log("onHttpRequestCompleted - Error buffer: %s", response->getErrorBuffer());
return;
}
log("onHttpRequestCompleted - Response code: %s", response->getResponseDataString());
std::vector<char> *buffer = response->getResponseData();
Image * image = new Image ();
image-> initWithImageData ( reinterpret_cast<const unsigned char*>(&(buffer->front())), buffer->size());
Texture2D * texture = new Texture2D ();
texture-> initWithImage (image);
Sprite* sp = Sprite::createWithTexture(texture);
add(sp);
sp->setPosition(Vec2(100, 100));
}

Related

How to make POST request to a web server with C++ and Core Foundation APIs for macOS?

I'm trying to follow this example to let me make a POST request to a web server and receive its response in pure C++ using Core Foundation functions. I'll copy and paste it here:
void PostRequest()
{
// Create the POST request payload.
CFStringRef payloadString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("{\"test-data-key\" : \"test-data-value\"}"));
CFDataRef payloadData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, payloadString, kCFStringEncodingUTF8, 0);
CFRelease(payloadString);
//create request
CFURLRef theURL = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("https://httpbin.org/post"), NULL); //https://httpbin.org/post returns post data
CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("POST"), theURL, kCFHTTPVersion1_1);
CFHTTPMessageSetBody(request, payloadData);
//add some headers
CFStringRef hostString = CFURLCopyHostName(theURL);
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("HOST"), hostString);
CFRelease(hostString);
CFRelease(theURL);
if (payloadData)
{
CFStringRef lengthString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), CFDataGetLength(payloadData));
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Length"), lengthString);
CFRelease(lengthString);
}
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Type"), CFSTR("charset=utf-8"));
//create read stream for response
CFReadStreamRef requestStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request);
CFRelease(request);
//set up on separate runloop (with own thread) to avoid blocking the UI
CFReadStreamScheduleWithRunLoop(requestStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
CFOptionFlags optionFlags = (kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered);
CFStreamClientContext clientContext = {0, (void *)payloadData, RetainSocketStreamHandle, ReleaseSocketStreamHandle, NULL};
CFReadStreamSetClient(requestStream, optionFlags, ReadStreamCallBack, &clientContext);
//start request
CFReadStreamOpen(requestStream);
if (payloadData)
{
CFRelease(payloadData);
}
}
And the callback:
void LogData(CFDataRef responseData)
{
CFIndex dataLength = CFDataGetLength(responseData);
UInt8 *bytes = (UInt8 *)malloc(dataLength);
CFDataGetBytes(responseData, CFRangeMake(0, CFDataGetLength(responseData)), bytes);
CFStringRef responseString = CFStringCreateWithBytes(kCFAllocatorDefault, bytes, dataLength, kCFStringEncodingUTF8, TRUE);
CFShow(responseString);
CFRelease(responseString);
free(bytes);
}
static void ReadStreamCallBack(CFReadStreamRef readStream, CFStreamEventType type, void *clientCallBackInfo)
{
CFDataRef passedInData = (CFDataRef)(clientCallBackInfo);
CFShow(CFSTR("Passed In Data:"));
LogData(passedInData);
//append data as we receive it
CFMutableDataRef responseBytes = CFDataCreateMutable(kCFAllocatorDefault, 0);
CFIndex numberOfBytesRead = 0;
do
{
UInt8 buf[1024];
numberOfBytesRead = CFReadStreamRead(readStream, buf, sizeof(buf));
if (numberOfBytesRead > 0)
{
CFDataAppendBytes(responseBytes, buf, numberOfBytesRead);
}
} while (numberOfBytesRead > 0);
//once all data is appended, package it all together - create a response from the response headers, and add the data received.
//note: just having the data received is not enough, you need to finish the response by retrieving the response headers here...
CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
if (responseBytes)
{
if (response)
{
CFHTTPMessageSetBody(response, responseBytes);
}
CFRelease(responseBytes);
}
//close and cleanup
CFReadStreamClose(readStream);
CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
CFRelease(readStream);
//just keep the response body and release requests
CFDataRef responseBodyData = CFHTTPMessageCopyBody(response);
if (response)
{
CFRelease(response);
}
//get the response as a string
if (responseBodyData)
{
CFShow(CFSTR("\nResponse Data:"));
LogData(responseBodyData);
CFRelease(responseBodyData);
}
}
I understood how it works, and started implementing it ..... only to get this error:
'CFReadStreamCreateForHTTPRequest' is deprecated: first deprecated in
macOS 10.11 - Use NSURLSession API for http requests
There's absolutely zero examples how to use NSURLSession for C++, or how to bypass that idiotic "is deprecated" error.
Any help on how am I supposed to code this in C++ now?
PS. I don't want to use any third-party libraries. This is a simple task that was available with simple API calls (as I showed above.)
PS2. Sorry I am not an Apple developer, and I'm not used to features being deprecated on the whim.
There are 3 options.
Ignore the warning.
Use ObjC runtme.
Use libcurl
The first one is the easiest and the second one is the hardest solutions for your skills. The third option is easy and the most advanced solution - if you extend you software with new features, CFNetwork will lack of functionality.

Very slow download from live server

Here is my controller...
public ActionResult RequestImage(string url, int width, int height)
{
Stream stream = fileService.Request(url, width, height);
return new FileStreamResult(stream, "image/jpg");
}
The file service...
public Stream Request(string url, int width, int height)
{
var request = new S3StorageRequest(url, null, null);
var stream = s3Service.Request(request);
var outputStream = new MemoryStream();
var settings = $"maxwidth={width}";
if (height > 0)
{
settings = string.Concat(settings, $"&maxheight={height}");
}
imageResizer.Build(stream, outputStream, settings);
stream.Dispose();
outputStream.Position = 0;
return outputStream;
}
S3 request...
public Stream Request(S3StorageRequest s3Request)
{
GetObjectRequest request = new GetObjectRequest { BucketName = bucketName };
request.Key = s3Request.Path;
GetObjectResponse response = S3Client.GetObject(request);
return response.ResponseStream;
}
When I run this locally (which is still downloading from S3 over the internet), the S3 request takes around 130ms (as low as 78ms).
The images resizing takes around 50ms.
Here is the request according to the browser...
On the live server, this is what the browser says...
Its a t2.micro instance, my Windows instance is running via Parallels on a 2.4Gz i5. Any ideas what it's doing for 2s? The TTFB can go down to around 854ms.
Without the image resizing and download, its about 450ms. So the image resizing or the file response seems to be slow.

How to get the large picture from feed with graph api?

When loading the Facebook feeds from one page, if a picture exist in the feed, I want to display the large picture.
How can I get with the graph API ? The picture link in the feed is not the large one.
Thanks.
The Graph API photo object has a picture connection (similar to that the user object has):
“The album-sized view of the photo. […] Returns: HTTP 302 redirect to the URL of the picture.”
So requesting https://graph.facebook.com/{object-id-from-feed}/picture will redirect you to the album-sized version of the photo immediately. (Usefull not only for displaying it in a browser, but also if f.e. you want to download the image to your server, using cURL with follow_redirect option set.)
Edit:
Beginning with API v2.3, the /picture edge for feed posts is deprecated.
However, as a field the picture can still be requested – but it will be a small one.
But full_picture is available as well.
So /{object-id-from-feed}?fields=picture,full_picture can be used to request those, or they can be requested directly with the rest of feed data, like this /page-id/feed?fields=picture,full_picture,… (additional fields, such as message etc., must be specified the same way.)
What worked for me :
getting the picture link from the feed and replacing "_s.jpg" with "_n.jpg"
OK, I found a better way. When you retrieve a feed with the graph API, any feed item with a type of photo will have a field called object_id, which is not there for plain status type items. Query the Graph API with that ID, e.g. https://graph.facebook.com/1234567890. Note that the object ID isn't an underscore-separated value like the main ID of that feed item is.
The result of the object_id query will be a new JSON dictionary, where you will have a source attribute containing a URL for an image that has so far been big enough for my needs.
There is additionally an images array that contains more image URLs for different sizes of the image, but the sizes there don't seem to be predictable, and don't all actually correspond to the physical dimensions of the image behind that URL.
I still wish there was a way to do this with a single Graph API call, but it doesn't look like there is one.
For high res image links from:
Link posts
Video posts
Photo posts
I use the following:
Note: The reason I give the _s -> _o hack precedence over the object_id/picture approach is because the object_id approach was not returning results for all images.
var picture = result.picture;
if (picture) {
if (result.type === 'photo') {
if (picture.indexOf('_s') !== -1) {
console.log('CONVERTING');
picture = picture.replace(/_s/, '_o');
} else if (result.object_id) {
picture = 'https://graph.facebook.com/' + result.object_id + '/picture?width=9999&height=9999';
}
} else {
var qps = result.picture.split('&');
for (var i = 0; i < qps.length; i++) {
var qp = qps[i];
var matches = qp.match(/(url=|src=)/gi);
if (matches && matches.length > 0) picture = decodeURIComponent(qp.split(matches[0])[1]);
}
}
}
This is a new method to get a big image. it was born after the previews method doesn't works
/**
* return a big url of facebook
* works onky for type PHOTO
* #param picture
* #param is a post type link
* #return url of image
*/
#Transactional
public String getBigImageByFacebookPicture(String pictrue,Boolean link){
if(link && pictrue.contains("url=http")){
String url = pictrue.substring(pictrue.indexOf("url=") + 4);
try {
url = java.net.URLDecoder.decode(url, "UTF-8");
} catch (UnsupportedEncodingException e) {
StringBuffer sb = new StringBuffer("Big image for Facebook link not found: ");
sb.append(link);
loggerTakePost.error(sb.toString());
return null;
}
return url;
}else{
try {
Document doc = Jsoup.connect(pictrue).get();
return doc.select("#fbPhotoImage").get(0).attr("src");
} catch (Exception e) {
StringBuffer sb = new StringBuffer("Big image for Facebook link not found: ");
sb.append(link);
loggerTakePost.error(sb.toString());
return null;
}
}
}
Enjoy your large image :)
Actually, you need two different solutions to fully fix this.
1] https://graph.facebook.com/{object_id}/picture
This solution works fine for images and videos posted to Facebook, but sadly, it returns small images in case the original image file was not uploaded to Facebook directly. (When posting a link to another site on your page for example).
2] The Facebook Graph API provides a way to get the full images in the feed itself for those external links. If you add 'full_picture' to the fields like in this example below when calling the API, you will be provided a link to the higher resolution version.
https://graph.facebook.com/your_facebook_id/posts?fields=id,link,full_picture,description,name&access_token=123456
Combining these two solutions I ended up filtering the input in PHP as follows:
if ( isset( $post['object_id'] ) ){
$image_url = 'https://graph.facebook.com/'.$post['object_id'].'/picture';
}else if ( isset( $post['full_picture'] ) ) {
$image_url = $post['full_picture'];
}else{
$image_url = '';
}
See: http://api-portal.anypoint.mulesoft.com/facebook/api/facebook-graph-api/docs/reference/pictures
Just put "?type=large" after the URL to get the big picture.
Thanks to #mattdlockyer for the JS solution. Here is a similar thing in PHP:
$posts = $facebook->api('/[page]/posts/', 'get');
foreach($posts['data'] as $post)
{
if(stristr(#$post['picture'], '_s.'))
{
$post['picture'] = str_replace('_s.', '_n.', #$post['picture']);
}
if(stristr(#$post['picture'], 'url='))
{
parse_str($post['picture'], $picturearr);
if($picturearr['url'])
$post['picture'] = $picturearr['url'];
}
//do more stuff with $post and $post['picture'] ...
}
After positive comment from #Lachezar Todorov I decided to post my current approach (including paging and using Json.NET ;):
try
{
FacebookClient fbClient = new FacebookClient(HttpContext.Current.Session[SessionFacebookAccessToken].ToString());
JObject posts = JObject.Parse(fbClient.Get(String.Format("/{0}/posts?fields=message,picture,link,attachments", FacebookPageId)).ToString());
JArray newsItems = (JArray)posts["data"];
List<NewsItem> result = new List<NewsItem>();
while (newsItems.Count > 0)
{
result.AddRange(GetItemsFromJsonData(newsItems));
if (result.Count > MaxNewsItems)
{
result.RemoveRange(MaxNewsItems, result.Count - MaxNewsItems);
break;
}
JToken paging = posts["paging"];
if (paging != null)
{
if (paging["next"] != null)
{
posts = JObject.Parse(fbClient.Get(paging.Value<String>("next")).ToString());
newsItems = (JArray)posts["data"];
}
}
}
return result;
}
And the helper method to retieve individual items:
private static IEnumerable<NewsItem> GetItemsFromJsonData(IEnumerable<JToken> items)
{
List<NewsItem> newsItems = new List<NewsItem>();
foreach (JToken item in items.Where(item => item["message"] != null))
{
NewsItem ni = new NewsItem
{
Message = item.Value<String>("message"),
DateTimeCreation = item.Value<DateTime?>("created_time"),
Link = item.Value<String>("link"),
Thumbnail = item.Value<String>("picture"),
// http://stackoverflow.com/questions/28319242/simplify-looking-up-nested-json-values-with-json-net/28359155#28359155
Image = (String)item.SelectToken("attachments.data[0].media.image.src") ?? (String)item.SelectToken("attachments.data[0].subattachments.data[0].media.image.src")
};
newsItems.Add(ni);
}
return newsItems;
}
NewsItem class I use:
public class NewsItem
{
public String Message { get; set; }
public DateTime? DateTimeCreation { get; set; }
public String Link { get; set; }
public String Thumbnail { get; set; }
public String Image { get; set; }
}

HTTP/1.1 200 213 on Tomcat while uploading jpeg file using RESTKit

All,
I am using RestKit for iOS to upload a JPEG file to my Java web service. I referred to this
tutorial for developing file upload web service and it works perfectly fine when i use it through my web browser.
However, when i try to upload a file using RESTKit then in TOMCAT logs i get HTTP/1.1 200 213 status code and my file is not uploaded.
Here is my RESTKit code:
RKObjectManager* manager = [RKObjectManager sharedManager];
RKObjectLoader* objectLoader = [manager objectLoaderWithResourcePath:#"/fileuploaded" delegate:self];
objectLoader.method = RKRequestMethodPOST;
UIImage *image = [UIImage imageNamed:#"rental_car_bill.jpeg"];
NSData *imageData = UIImageJPEGRepresentation(image, 1.0);
// attach image
RKParams *params = [RKParams paramsWithDictionary:(NSDictionary*)objectLoader.params];
RKParamsAttachment *attachment = [params setData:imageData
MIMEType:#"image/jpeg" forParam:#"photo"];
attachment.fileName = #"samplejpeg";
objectLoader.params = params;
objectLoader.targetObject = self;
[objectLoader send];
EDIT:
Above implementation does work and the file does get uploaded. However, in the delegate method: - (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects
After it gets out of the scope of this, then my application crashes at [RKObjectLoader dealloc];
To upload pictures using restkit I use the following methods, I prefer using blocks to avoid some problems on my app, maybe that way you can avoid your crash:
- (void) upload: (KFMedia *) pic onLoad:(RKObjectLoaderDidLoadObjectBlock) loadBlock onError:(RKRequestDidFailLoadWithErrorBlock)failBlock{
//pic.image returns an UIImage
RKParams* imageParams = [RKParams params];
NSData* imageData = UIImageJPEGRepresentation(pic.image, 0.7f);
[imageParams setData:imageData MIMEType:#"image/jpg" forParam:#"FileUpload"];
NSString *resourcePath = #"/api/upload/";
//My API Server will return a JSON that represents my KFMedia Class after uploading the image, so here I get the propper mapping for that
RKObjectMapping *mapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[KFMedia class]];
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:resourcePath usingBlock:^(RKObjectLoader *loader) {
loader.method = RKRequestMethodPOST;
loader.params = imageParams;
[self settingsForLoader:loader withMapping:mapping onLoad:loadBlock onError:failBlock];
}];
}
- (void) settingsForLoader: (RKObjectLoader *) loader withMapping: (RKObjectMapping *) mapping onLoad:(RKObjectLoaderDidLoadObjectBlock) loadBlock onError:(RKRequestDidFailLoadWithErrorBlock)failBlock{
loader.objectMapping = mapping;
loader.delegate = self;
loader.onDidLoadObject = loadBlock;
loader.onDidFailWithError = ^(NSError * error){
NSLog(#"%#",error);
};
loader.onDidFailLoadWithError = failBlock;
loader.onDidLoadResponse = ^(RKResponse *response) {
[self fireErrorBlock:failBlock onErrorInResponse:response];
};
}

trying to upload video using graph api

I have been trying to implement a video upload facebook feature for my mobile app for a while now but never really succeeded with rest. I learnt yesterday that the graph alternative was available.
After getting a few errors to do with access key mainly i have gotten to the point where output stream succesfully writes the movie file and the input stream just receives an empty json array once i have written the 3gp file.
Anyone any idea why I would get an empty json array and no video gets published when i get all my code to run, i get 200 response code and the server sends me a non error response?
Any help greatly appreciated.
Here is the class that gets the blank json array (send method). I have appended the token to the url and in the table to be sure. I am sorry if the code is untidy buts it just been a day of trial and error.
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import net.rim.device.api.io.http.HttpProtocolConstants;
public class HttpMultipartRequest2
{
static final String BOUNDARY = "----------V2ymHFg03ehbqgZCaKO6jy";
byte[] postBytes = null;
String url = null;
Hashtable paramsTable;
public HttpMultipartRequest2(String url, Hashtable params,
String fileField, String fileName, String fileType, byte[] fileBytes) throws Exception
{
this.url = url;
String boundary = getBoundaryString();
paramsTable = params;
String boundaryMessage = getBoundaryMessage(boundary, params, fileField, fileName, fileType);
String endBoundary = "\r\n--" + boundary + "--\r\n";
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(boundaryMessage.getBytes());
bos.write(fileBytes);
bos.write(endBoundary.getBytes());
this.postBytes = bos.toByteArray();
bos.close();
}
String getBoundaryString() {
return BOUNDARY;
}
String getBoundaryMessage(String boundary, Hashtable params, String fileField, String fileName, String fileType)
{
StringBuffer res = new StringBuffer("--").append(boundary).append("\r\n");
Enumeration keys = params.keys();
while(keys.hasMoreElements())
{
String key = (String)keys.nextElement();
String value = (String)params.get(key);
res.append("Content-Disposition: form-data; name=\"").append(key).append("\"\r\n")
.append("\r\n").append(value).append("\r\n")
.append("--").append(boundary).append("\r\n");
}
res.append("Content-Disposition: form-data; name=\"").append(fileField)
.append("\"; filename=\"").append(fileName).append("\"\r\n")
.append("Content-Type: ").append(fileType).append("\r\n\r\n");
Log.info(("res "+res.toString()));
return res.toString();
}
public String send() throws Exception
{
StringBuffer sb = new StringBuffer();
HttpConnection hc = null;
InputStream is = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] res = null;
try
{
Log.info("before hc open"+ url);
hc = (HttpConnection) Connector.open(url);
hc.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + getBoundaryString());
hc.setRequestProperty("access_token", (String)paramsTable.get("access_token"));
hc.setRequestProperty(HttpProtocolConstants.HEADER_CONTENT_LENGTH, String.valueOf(postBytes.length));
hc.setRequestProperty( "x-rim-transcode-content", "none" );
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutputStream dos = hc.openOutputStream();
is = hc.openInputStream();
Log.info("before dos write responsecode");// + hc.getResponseCode());
out.write(postBytes, 0, postBytes.length);
//Log.info("flushing"+ hc.getResponseCode());
Log.info("after doswrite responsecode");
dos.write(out.toByteArray());
dos.flush();
Log.info("after flush");
if(dos!=null)
dos.close();
int ch;
Log.info("before openinput ");
Log.info("after openinput ");
while ((ch = is.read()) != -1)
{
bos.write(ch);
sb.append((char)ch);
Log.info("char"+(char)ch);
}
res = bos.toByteArray();
Log.info("Response recieved from Server is : " + sb.toString() );
}
catch(Exception e)
{
Log.info(hc.getResponseCode() + "sexce"+e);
}
catch(OutOfMemoryError error)
{
Log.info("outofmemory " + error);
System.gc();
}
finally
{
try
{
if(bos != null)
bos.close();
if(is != null)
is.close();
if(hc != null)
hc.close();
}
catch(Exception e2)
{
Log.info("finally exception"+ e2);
}
}
return sb.toString();
}
}
Are you trying to upload to a user's feed or to a page? There is an Open Bug regarding posting to pages.
Also, could you post some code?
Assuming that you've read the documentation:
Facebook Graph API->Video
And that you are using graph-video.facebook.com, not graph.facebook.com.