Facebook Graph API - загрузка фотографий с использованием JavaScript

Можно ли загрузить файл с помощью API-интерфейса Facebook, используя javascript, я чувствую, что я рядом. Я использую следующий JavaScript

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!');
        }
    }); 
}

При запуске я получаю следующую ошибку...

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

Когда я пытаюсь исследовать этот метод, все, что я могу узнать, это метод php, который пытается решить эту проблему.

$facebook->setFileUploadSupport(true);

Тем не менее, я использую JavaScript, похоже, этот метод может быть связан с разрешениями Facebook Graph, но я уже установил разрешения user_photos и publish_stream, которые, как я полагал, являются единственными, которые мне нужны для выполнения этой операции.

Я видел пару неотвеченных вопросов относительно этого в stackoverflow, надеюсь, я могу объяснить себя достаточно. Спасибо, ребята.

Ответы

Ответ 1

EDIT: этот ответ (сейчас) в значительной степени не имеет значения. Если ваше изображение находится в Интернете, просто укажите параметр url в соответствии с API (и см. Примеры в других ответах). Если вы хотите, чтобы POST-контент был напрямую связан с facebook, вы можете прочитать этот ответ, чтобы получить понимание. Также см. HTML5 Canvas.toDataUrl().

API говорит: "Чтобы опубликовать фотографию, отправьте запрос POST с прикрепленным файлом фото в формате multipart/form-data".

FB ожидает, что байты загружаемого изображения находятся в теле HTTP-запроса, но их там нет. Или посмотреть на это по-другому - где в вызове FB.api() вы загружаете фактическое содержимое самого изображения?

API FB.api() плохо документирован и не предоставляет пример HTTP POST, который включает тело. Из отсутствия такого примера можно сделать вывод, что он не поддерживает это.

Скорее всего, OK - FB.api() использует что-то под названием XmlHttpRequest под обложками, которое поддерживает, включая тело,... посмотрите его в своей любимой ссылке на JavaScript.

Однако у вас все еще есть 2 проблемы:

  • как подготовить байты изображения (и остальную часть запроса) как multipart/form-data; и
  • получение байтов самого изображения

(кстати, необходимость кодирования тела сообщения, вероятно, является методом PHP setFileUploadSupport (true) для - сообщает объекту facebook кодировать тело сообщения как multipart/form-data перед отправкой)

Но это немного меньше, чем

К сожалению, суб-проблема "2" может вас укусить - нет способа (в последний раз, когда я смотрел) извлечь байты изображения из предоставленного браузером объекта Image.

Если загружаемое изображение доступно через URL-адрес, вы можете получить байты с помощью XmlHttpRequest. Не так уж плохо.

Если изображение поступает с рабочего стола пользователя, ваш вероятный регресс должен предложить пользователю:

 <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> 

(обратите внимание, что source ссылается на имя, указанное в виджетах загрузки файлов)

... и надеемся, что FB ожидает получения данных таким образом (сначала попробуйте его со статической формой HTML, прежде чем кодировать его динамически в JS). Можно было бы сделать вывод, что на самом деле это было бы, поскольку они не предлагают другого средства для этого.

Ответ 2

Да, это возможно, я нахожу 2 решения, как это сделать, и они очень похожи друг другу, нужно просто определить параметр url для внешнего URL изображения

ПЕРВЫЙ, используя 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);
    }

});

и ВТОРОЙ, используя jQuery Post request и 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");
                    }
                });

Ответ 3

я использовал код @Владимир Дворник с некоторой модификацией, у меня была такая же проблема, и с этим кодом он работал очень хорошо:

        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);
            }

        });

Ответ 4

Фотографии можно загрузить в профиль facebook с помощью Ajax следующим образом.

$.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"); }
            });

Итак, это лучший способ отправить фотографию в профиль facebook с помощью GRAPH API и является простым.

Во многих ответах я видел, что URL изображения является shwon источником, изображением или изображением и т.д., но это не работает.

Использование источника, изображения или изображения приводит к (# 324) Требуется загрузить файл.

Лучший способ избежать ошибки 324.

Ответ 5

Только ответ @Thiago отвечает на вопрос о загрузке данных через javascript. Я обнаружил, что API JS для Facebook не охватывает эту ситуацию.

У меня также есть brew и проверено мое решение personl.

Основные шаги

  • Получить двоичные данные изображения (я использовал холст, но также можно использовать поле ввода)
  • Формируйте многостраничный запрос со всеми необходимыми данными для вызова API-интерфейса графика
  • Включить двоичные данные в запрос
  • Кодировать все в двоичном массиве и отправлять его через XHR

Код

Утилиты преобразования

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

Снимок публикации изображений

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);
};

Использование

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
  }
});

Extra

Извлечение двоичного строкового представления изображения холста

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);
}

Информация о загрузке бинарной строки из input[type=file]

API-интерфейс HTML5 читается как текстовый и двоичный

Примечания:

  • Есть, конечно, альтернативные подходы
    • Использование формы HTML в iframe - вы не можете получить ответ от вызова
    • Использование FormData и File, но, к сожалению, в этом случае есть много несовместимостей, которые усложняют процесс, и вы в конечном итоге обходите каналограмму вокруг несоответствий - таким образом, мой выбор был ручной сборкой данных, поскольку стандарты HTTP редко меняются:)
  • Решение не требует специальных функций HTML5.
  • В приведенном выше примере используется jQuery.ajax, jQuery.extend, jQuery.each

Ответ 6

Да, вы можете сделать эти данные для отправки в iframe, например здесь, или вы можете использовать Загрузка файла jQuery. Проблема в том, что вы не можете получить ответ от iframe, используя плагин, вы можете использовать дескриптор страницы.

Пример: загрузка видео с помощью загрузки файла jQuery

<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>

Ответ 7

Чтобы загрузить файл с локального компьютера с помощью Javascript, попробуйте 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>

Там загрузите демо на http://adodson.com/hello.js/demos/upload.html

Ответ 8

fooobar.com/questions/7100/... содержит решение, которое работает, если вам нужно загрузить сами фотографии и не иметь URL-адреса.

Ответ 9

Это все еще работает. Я использую его, как показано ниже:

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);
}

Я должен спросить, если у вас есть идеи, если это целесообразно или имеет большой риск для безопасности по сравнению с использованием PHP api для Facebook.

Ответ 10

Это работает:

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

    // let 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);
};

Ответ 11

У меня, похоже, есть аналогичная проблема, но решения выше не работают.

Я использовал решение, предложенное Arrabi (просто используйте свойство url), чтобы отправлять изображения без каких-либо проблем. Мои изображения составляют около 2-3 МБ каждый.

Когда я перенес свое приложение на другой сервер (изменив абсолютный URL-адрес моих изображений в сообщении), метод продолжал давать мне 324 ошибки для изображений размером около 100 тыс..

Я думал, что это связано с некоторыми настройками Apache на моем конце, но когда я изменил apache для lighttpd, проблема все еще была.

Связи из Facebook фактически отображаются в моем (apache) журнале:

69.171.234.7 - - [08/Июнь/2012: 11: 35: 54 +0200] "GET/images/cards/1337701633_518192458.png HTTP/1.1" 200 2676608 "-" "facebookplatform/1.0 (+ http://developers.facebook.com)"

69.171.228.246 - - [08/Июнь/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/Июнь/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/Июнь/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/Июнь/2012: 11: 42: 31 +0200] "GET/images/test6.png HTTP/1.1" 200 727722 "-" "facebookplatform/1.0 (+ http://developers.facebook.com)"

Выполнено только test2.png.

Ответ 12

Я использую следующее, чтобы поделиться фотографией (некоторые BitmapData из фреймовой рамки). Кажется, это работает...

// 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);
}

Ответ 13

В случае, если кто-то еще ищет, как загружать прямо из холста на фотографии Facebook, это работает для меня:

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'});
}

Использовать его

// *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});
  }
});

Изменено: http://gorigins.com/posting-a-canvas-image-to-facebook-and-twitter/