web-dev-qa-db-fra.com

Facebook Graph API - télécharger une photo en utilisant JavaScript

Est-il possible de télécharger un fichier à l'aide de l'API Facebook Graph à l'aide de javascript, j'ai l'impression d'être proche. J'utilise le JavaScript suivant

var params = {};
params['message'] = 'PicRolled';
params['source'] = '@'+path;
params['access_token'] = access_token;
params['upload file'] = true;

function saveImage() {
    FB.api('/me/photos', 'post', params, function(response) {
        if (!response || response.error) {
            alert(response);
        } else {
            alert('Published to stream - you might want to delete it now!');
        }
    }); 
}

En cours d'exécution, je reçois le message d'erreur suivant ...

"OAuthException" - "(#324) Requires upload file"

Quand j'essaie de faire des recherches sur cette méthode, je ne découvre qu'une méthode php qui permet de résoudre ce problème.

$facebook->setFileUploadSupport(true);

Cependant, j'utilise JavaScript. Il semble que cette méthode soit liée aux autorisations de Facebook Graph, mais j'ai déjà défini les autorisations user_photos et publish_stream, qui, à mon avis, sont les seules dont je devrais avoir besoin pour effectuer cette opération.

J'ai vu quelques questions sans réponse à ce sujet à propos du stackoverflow, j'espère pouvoir m'expliquer suffisamment. Merci les gars.

40
Moz

EDIT: cette réponse est (maintenant) largement hors de propos. Si votre image est sur le Web, spécifiez simplement le paramètre url conformément au API (et voyez des exemples dans d'autres réponses). Si vous souhaitez POST afficher directement le contenu de l'image sur Facebook, vous pouvez lire cette réponse pour mieux comprendre. Voir aussi la fonction Canvas.toDataUrl() de HTML5.

L'API dit : "Pour publier une photo, lancez une demande POST avec la pièce jointe du fichier photo sous forme de multipart/form-data."

FB s'attend à ce que les octets de l'image à télécharger figurent dans le corps de la demande HTTP, mais ils ne le sont pas. Ou bien, regardez-le sous un autre angle: où, dans l'appel FB.api (), fournissez-vous le contenu réel de l'image? 

L'API FB.api () est mal documentée et ne fournit pas d'exemple de HTTP POST incluant un corps. On pourrait déduire de l'absence d'un tel exemple que cela ne le supporte pas.

C'est probablement OK - FB.api () utilise quelque chose appelé XmlHttpRequest sous les couvertures que ne prend en charge, y compris un corps ... regardez-le dans votre référence JavaScript préférée.

Cependant, vous aurez toujours 2 problèmes secondaires à résoudre:

  1. comment préparer les octets de l'image (et le reste de la demande) sous la forme multipart/form-data ; et
  2. obtenir les octets de l'image elle-même

(incidemment, la nécessité de coder le corps du message correspond probablement à l'usage de la méthode PHP setFileUploadSupport (true) - indique à l'objet facebook de coder le corps du message sous la forme multipart/form-data avant de l'envoyer)

Mais c'est un peu plus que ça

Malheureusement, le sous-problème '2' peut vous mordre - il n'y a aucun moyen (la dernière fois que j'ai regardé) d'extraire les octets d'une image à partir de l'objet Image fourni par le navigateur. 

Si l'image à télécharger est accessible via une URL, vous pouvez récupérer les octets avec XmlHttpRequest. Pas mal.

Si l'image provient du bureau de l'utilisateur, votre recours probable est d'offrir à l'utilisateur un:

 <form enctype="multipart/form-data">
     <input type="filename" name="myfile.jpg" />
     <input type="hidden" name="source" value="@myfile.jpg"/>
     <input type="hidden" name="message" value="My Message"/>
     <input type="hidden" name="access_token" value="..."/> 
 </form> 

(notez que source fait référence au nom donné au widget de téléchargement de fichier)

... et espère que FB a prévu de recevoir les données de cette manière (essayez-le d'abord avec un formulaire HTML statique avant de le coder de manière dynamique dans JS). On pourrait en déduire que ce serait le cas, puisqu'ils n'offrent pas un autre moyen de le faire.

26
David Bullock

Oui, c’est possible, je trouve 2 solutions pour faire cela et elles sont très similaires L’un pour l’autre, vous avez juste besoin de définir le paramètre url à l’image externe url

PREMIER en utilisant Javascript SDk:

var imgURL="http://farm4.staticflickr.com/3332/3451193407_b7f047f4b4_o.jpg";//change with your external photo url
FB.api('/album_id/photos', 'post', {
    message:'photo description',
    url:imgURL        
}, function(response){

    if (!response || response.error) {
        alert('Error occured');
    } else {
        alert('Post ID: ' + response.id);
    }

});

et SECOND one en utilisant jQuery Post request et FormData:

 var postMSG="Your message";
 var url='https://graph.facebook.com/albumID/photos?access_token='+accessToken+"&message="+postMSG;
 var imgURL="http://farm4.staticflickr.com/3332/3451193407_b7f047f4b4_o.jpg";//change with your external photo url
 var formData = new FormData();
 formData.append("url",imgURL);

  $.ajax({
                    url: url,
                    data: formData,
                    cache: false,
                    contentType: false,
                    processData: false,
                    type: 'POST',

                    success: function(data){
                        alert("POST SUCCESSFUL");
                    }
                });

j'ai utilisé le code @ Владимир Дворник avec quelques modifications, j'avais le même problème et avec ce code cela fonctionnait très bien:

        var imgURL = //your external photo url
        FB.api('/photos', 'post', {
            message: 'photo description',
            access_token: your accesstoken 
            url: imgURL
        }, function (response) {

            if (!response || response.error) {
                alert('Error occured:' + response);
            } else {
                alert('Post ID: ' + response.id);
            }

        });
17
Arrabi

Les photos peuvent être téléchargées sur le profil facebook en utilisant Ajax comme suit.

$.ajax({
            type: "POST",
            url: "https://graph.facebook.com/me/photos",
            data: {
                message: "Your Msg Goes Here",
                url: "http://www.knoje.com/images/photo.jpg[Replace with yours]",
                access_token: token,
                format: "json"
            },
            success: function(data){
               alert("POST SUCCESSFUL"); }
            });

C’est donc le moyen le plus simple d’afficher une photo sur un profil Facebook avec l’API GRAPH.

Dans beaucoup de réponses j'ai vu que l'URL de l'image est shwon par la source, l'image ou l'image etc. mais cela ne fonctionne pas. 

L'utilisation de source, image ou image conduit à un (# 324) Nécessite le téléchargement du fichier error.

Le meilleur moyen d'éviter l'erreur 324.

11
sumitkanoje

Seule la réponse de @ Thiago répond à la question de uploader des données via javascript. J'ai constaté que l'API Facebook JS ne couvre pas cette situation. 

J'ai également préparé et testé ma solution personnelle. 

Étapes principales

  1. Obtenir les données binaires de l'image (j'ai utilisé un canevas, mais l'utilisation d'une zone de saisie est également possible)
  2. Former une requête en plusieurs parties avec toutes les données necesarry pour l'appel d'API de graphique
  3. Inclure les données binaires dans la demande
  4. Encode tout dans un tableau binaire et l'envoie via XHR

Code

Utilitaires de conversion

var conversions = {
  stringToBinaryArray: function(string) {
    return Array.prototype.map.call(string, function(c) {
      return c.charCodeAt(0) & 0xff;
    });
  },
  base64ToString: function(b64String) {
    return atob(b64String);
  }
};

Extrait de publication d'image

var DEFAULT_CALL_OPTS = {
  url: 'https://graph.facebook.com/me/photos',
  type: 'POST',
  cache: false,
  success: function(response) {
    console.log(response);
  },
  error: function() {
    console.error(arguments);
  },
  // we compose the data manually, thus
  processData: false,
  /**
   *  Override the default send method to send the data in binary form
   */
  xhr: function() {
    var xhr = $.ajaxSettings.xhr();
    xhr.send = function(string) {
      var bytes = conversions.stringToBinaryArray(string);
      XMLHttpRequest.prototype.send.call(this, new Uint8Array(bytes).buffer);
    };
    return xhr;
  }
};
/**
 * It composes the multipart POST data, according to HTTP standards
 */
var composeMultipartData = function(fields, boundary) {
  var data = '';
  $.each(fields, function(key, value) {
    data += '--' + boundary + '\r\n';

    if (value.dataString) { // file upload
      data += 'Content-Disposition: form-data; name=\'' + key + '\'; ' +
        'filename=\'' + value.name + '\'\r\n';
      data += 'Content-Type: ' + value.type + '\r\n\r\n';
      data += value.dataString + '\r\n';
    } else {
      data += 'Content-Disposition: form-data; name=\'' + key + '\';' +
        '\r\n\r\n';
      data += value + '\r\n';
    }
  });
  data += '--' + boundary + '--';
  return data;
};

/**
 * It sets the multipart form data & contentType
 */
var setupData = function(callObj, opts) {
  // custom separator for the data
  var boundary = 'Awesome field separator ' + Math.random();

  // set the data
  callObj.data = composeMultipartData(opts.fb, boundary);

  // .. and content type
  callObj.contentType = 'multipart/form-data; boundary=' + boundary;
};

// the "public" method to be used
var postImage = function(opts) {

  // create the callObject by combining the defaults with the received ones
  var callObj = $.extend({}, DEFAULT_CALL_OPTS, opts.call);

  // append the access token to the url
  callObj.url += '?access_token=' + opts.fb.accessToken;

  // set the data to be sent in the post (callObj.data = *Magic*)
  setupData(callObj, opts);

  // POST the whole thing to the defined FB url
  $.ajax(callObj);
};

Usage

postImage({
  fb: { // data to be sent to FB
    caption: caption,
    /* place any other API params you wish to send. Ex: place / tags etc.*/
    accessToken: 'ACCESS_TOKEN',
    file: {
      name: 'your-file-name.jpg',
      type: 'image/jpeg', // or png
      dataString: image // the string containing the binary data
    }
  },
  call: { // options of the $.ajax call
    url: 'https://graph.facebook.com/me/photos', // or replace *me* with albumid
    success: successCallbackFunction,
    error: errorCallbackFunction
  }
});

Supplémentaire

Extraction de la représentation sous forme de chaîne binaire d'une image de canevas

var getImageToBeSentToFacebook = function() {
  // get the reference to the canvas
  var canvas = $('.some-canvas')[0];

  // extract its contents as a jpeg image
  var data = canvas.toDataURL('image/jpeg');

  // strip the base64 "header"
  data = data.replace(/^data:image\/(png|jpe?g);base64,/, '');

  // convert the base64 string to string containing the binary data
  return conversions.base64ToString(data);
}

Informations sur le chargement de la chaîne binaire à partir d'un input[type=file]

API de fichier HTML5 lue sous forme de texte et binaire

Remarques:

  1. Il existe bien sûr d'autres approches
    • Utilisation d'un formulaire HTML dans une iframe - vous ne pouvez pas obtenir la réponse de l'appel
    • Utiliser une approche FormData & File , mais malheureusement, dans ce cas, il existe de nombreuses incompatibilités qui rendent le processus plus difficile à utiliser, et vous finissez par scotcher les conduits autour des incohérences - mon choix a donc été données manuelles Assemblage puisque les normes HTTP changent rarement :)
  2. La solution ne nécessite aucune fonctionnalité HTML5 particulière.
  3. L'exemple ci-dessus utilise jQuery.ajax , jQuery.extend , jQuery.each
6
Matyas

Oui, vous pouvez faire cela en publiant des données sur un iframe comme ici , ou vous pouvez utiliser jQuery File Upload . Le problème est que vous ne pouvez pas obtenir de réponse de iframe, vous pouvez utiliser le plugin un handle de page.

Exemple: télécharger une vidéo à l'aide de jQuery File Upload

<form id="fileupload" action="https://graph-video.facebook.com/me/photos" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="acess_token" value="user_acess_token">
    <input type="text" name="title">
    <input type="text" name="description">
    <input type="file" name="file"> <!-- name must be file -->
</form>


<script type="text/javascript">

    $('#fileupload').fileupload({
        dataType: 'json',
        forceIframeTransport: true, //force use iframe or will no work
        autoUpload : true,
        //facebook book response will be send as param
        //you can use this page to save video (Graph Api) object on database
        redirect : 'http://pathToYourServer?%s' 
    });
</script>
4

Pour télécharger un fichier à partir de l'ordinateur local avec Javascript, essayez HelloJS

<form onsubmit="upload();">
     <input type="file" name="file"/>
     <button type="submit">Submit</button>
</form>

<script>
function upload(){
   hello.api("facebook:/me/photos", 'post', document.getElementById('form'), function(r){
      alert(r&&!r.error?'Success':'Failed'); 
   });
}
</script>

Une démo de téléchargement est disponible sur http://adodson.com/hello.js/demos/upload.html

2
Drew

https://stackoverflow.com/a/16439233/68210 contient une solution qui fonctionne si vous devez télécharger les données photo elles-mêmes sans disposer d'une URL.

2
Daniel X Moore

Cela marche:

   function x(authToken, filename, mimeType, imageData, message) {
    // this is the multipart/form-data boundary we'll use
    var boundary = '----ThisIsTheBoundary1234567890';

    // let's encode our image file, which is contained in the var
    var formData = '--' + boundary + '\r\n';
    formData += 'Content-Disposition: form-data; name="source"; filename="' + filename + '"\r\n';
    formData += 'Content-Type: ' + mimeType + '\r\n\r\n';
    for (var i = 0; i < imageData.length; ++i) {
        formData += String.fromCharCode(imageData[i] & 0xff);
    }
    formData += '\r\n';
    formData += '--' + boundary + '\r\n';
    formData += 'Content-Disposition: form-data; name="message"\r\n\r\n';
    formData += message + '\r\n';
    formData += '--' + boundary + '--\r\n';

    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'https://graph.facebook.com/me/photos?access_token=' + authToken, true);
    xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary);

    // Solving problem with sendAsBinary for chrome
    try {
        if (typeof XMLHttpRequest.prototype.sendAsBinary == 'undefined') {
            XMLHttpRequest.prototype.sendAsBinary = function(text) {
                var data = new ArrayBuffer(text.length);
                var ui8a = new Uint8Array(data, 0);
                for (var i = 0; i < text.length; i++) ui8a[i] = (text.charCodeAt(i) & 0xff);
                this.send(ui8a);
            }
        }
    } catch (e) {}
    xhr.sendAsBinary(formData);
};
2
Thiago Tecedor

Cela fonctionne toujours. Je l'utilise comme ci-dessous:

var formdata= new FormData();
if (postAs === 'page'){
    postTo = pageId; //post to page using pageID
}

formdata.append("access_token", accessToken); //append page access token if to post as page, uAuth|paAuth
formdata.append("message", photoDescription);
formdata.append("url", 'http://images/image.png');

try {
    $.ajax({
        url: 'https://graph.facebook.com/'+ postTo +'/photos',
        type: "POST",
        data: formdata,
        processData: false,
        contentType: false,
        cache: false,
        error: function (shr, status, data) {
            console.log("error " + data + " Status " + shr.status);
        },
        complete: function () {
            console.log("Successfully uploaded photo to Facebook");
        }
    });
} catch (e) {
    console.log(e);
}

Je dois demander cependant si vos gens savent si cela est conseillé ou présente un risque important pour la sécurité par rapport à l'utilisation de PHP api pour Facebook.

2
Carmela

Je semble avoir un problème similaire, mais les solutions ci-dessus n'ont pas fonctionné.

J'utilisais la solution suggérée par Arrabi (utilisez uniquement la propriété url) pour publier des images sans aucun problème. Mes images sont environ 2-3 MB chacun.

Lorsque j'ai migré mon application vers un autre serveur (en modifiant l'URL absolue de mes images dans l'article), la méthode ne cessait de me donner 324 erreurs pour des images d'une taille supérieure à 100 000 environ.

Je pensais que cela était dû à certains réglages d’Apache, mais lorsque j’ai changé d’Apache pour lighttpd, le problème était toujours là.

Les connexions de Facebook apparaissent dans mon journal (Apache):

69.171.234.7 - - [08/Jun/2012: 11:35: 54 +0200] "GET /images/cards/1337701633_518192458.png HTTP/1.1" 200 2676608 "-" "facebookplatform/1.0 (+ http: // développeurs .facebook.com) "

69.171.228.246 - - [08/Jun/2012: 11: 42: 59 +0200] "GET /images/test5.jpg HTTP/1.1" 200 457402 "-" "facebookplatform/1.0 (+ http: //developers.facebook .com) "

69.171.228.246 - - [08/Jun/2012: 11: 43: 17 +0200] "GET /images/test4.jpg HTTP/1.1" 200 312069 "-" "facebookplatform/1.0 (+ http: //developers.facebook .com) "

69.171.228.249 - - [08/Jun/2012: 11: 43: 49 +0200] "GET /images/test2.png HTTP/1.1" 200 99538 "-" "facebookplatform/1.0 (+ http: //developers.facebook .com) "

69.171.228.244 - - [08/Jun/2012: 11: 42: 31 +0200] "GET /images/test6.png HTTP/1.1" 200 727722 "-" "facebookplatform/1.0 (+ http: //developers.facebook .com) "

Seul test2.png a réussi.

1
Tom Lous

J'utilise ce qui suit pour partager une photo (certains BitmapData du framework Phaser). Cela semble fonctionner ...

// Turn data URI to a blob ready for upload.
dataURItoBlob(dataURI:string): Blob {
    var byteString = atob(dataURI.split(',')[1]);
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }
    return new Blob([ab], { type: 'image/jpeg' });
}

// Share the given bitmapData as a photo on Facebook
sharePhoto(accessToken: string, photo: BitmapData, message: string): void {
    // Create form data, set up access_token, source and message
    var fd = new FormData();
    fd.append("access_token", accessToken);
    fd.append("source", this.dataURItoBlob(photo.canvas.toDataURL("image/jpeg")));
    fd.append("message", message);

    var request = new XMLHttpRequest();
    var thisPtr = this;
    request.onreadystatechange = function () {
        if (request.readyState == XMLHttpRequest.DONE) {
            var json = JSON.parse(request.responseText);
            if (json.hasOwnProperty("error")) {
                var error = json["error"];
                if (error.hasOwnProperty("type")) {
                    var errorType = error["type"];
                    if (errorType === "OAuthException") {
                        console.log("Need to request more permissions!");
                    }
                }
            }
        } else if (request.readyState == XMLHttpRequest.HEADERS_RECEIVED) {

        } else if (request.readyState == XMLHttpRequest.LOADING) {

        } else if (request.readyState == XMLHttpRequest.OPENED) {

        } else if (request.readyState == XMLHttpRequest.UNSENT) {

        }
    }
    request.open("POST", "https://graph.facebook.com/me/photos", true);
    request.send(fd);
}
0
Steven Craft

Si quelqu'un cherche encore comment télécharger directement de la toile sur des photos Facebook, cela fonctionne pour moi:

function postImageToFacebook(token, imageData, message, successCallback, errorCallback) {
  var fd = new FormData();
  fd.append("access_token", token);
  fd.append("source", imageData);
  fd.append("caption", message);

  $.ajax({
      url: "https://graph.facebook.com/me/photos?access_token=" + token,
      type: "POST",
      data: fd,
      processData: false,
      contentType: false,
      cache: false,
      success: function (data) {
        successCallback(data);
      },
      error: function (shr, status, data) {
        errorCallback(data);
      },
      complete: function (data) {
        console.log('Completed');
      }
  });
}

function dataURItoBlob(dataURI) {
  var byteString = atob(dataURI.split(',')[1]);
  var ab = new ArrayBuffer(byteString.length);
  var ia = new Uint8Array(ab);
  for (var i = 0; i < byteString.length; i++) {
      ia[i] = byteString.charCodeAt(i);
  }
  return new Blob([ab], {type: 'image/jpeg'});
}

Pour l'utiliser

// *IMPORTANT*
var FBLoginScope = 'publish_actions'; // or sth like 'user_photos,publish_actions' if you also use other scopes.

var caption = "Hello Facebook!";
var successCallback = ...;
var errorCallback = ...;

var data = $('#your_canvas_id')[0].toDataURL("image/jpeg");
try {
  imageData = dataURItoBlob(data);
} catch (e) {
  console.log(e);
}

FB.getLoginStatus(function (response) {
  if (response.status === "connected") {
    postImageToFacebook(response.authResponse.accessToken, imageData, caption, successCallback, errorCallback);
  } else if (response.status === "not_authorized") {
    FB.login(function (response) {
        postImageToFacebook(response.authResponse.accessToken, imageData, caption, successCallback, errorCallback);
    }, {scope: FBLoginScope});
  } else {
    FB.login(function (response) {
        postImageToFacebook(response.authResponse.accessToken, imageData, caption, successCallback, errorCallback);
    }, {scope: FBLoginScope});
  }
});

Modifié depuis: http://gorigins.com/posting-a-canvas-image-to-facebook-and-Twitter/

0
aunnnn