Baixe o objeto ContentVersion como JPG usando JSForce

Aug 23 2020

Estou tentando baixar um arquivo JPG armazenado como um anexo no Salesforce, para a máquina local. Eu tenho uma URL que funciona corretamente ao usar uma solicitação GET no Workbench. Quando tento fazer o mesmo no Node.JS (usando JSForce), não consigo fazer com que o arquivo saia como JPG.

Isso é o que eu tenho:

var jsforce = require('jsforce');
var conn = new jsforce.Connection();
conn.login('<username>', '<password>', function(err, res) {
  if (err) { return console.error(err); }  
conn.request('https://<my-dev-ed-instance>.my.salesforce.com/services/data/v42.0/sobjects/ContentVersion/<uuid>/VersionData', function(err, res) {
 if (err) { return console.error(err); }
 console.log(res);
 fs.writeFileSync('2.jpg',res,'base64');
});
});

Acredito que a solicitação GET deva retornar uma string BASE64, mas não consigo convertê-la ou torná-la útil.

Qualquer ajuda seria muito apreciada!

EDIT: Eu encontrei uma solução. Tbh, eu realmente precisava dormir um pouco e acordar e usar o Postman para solucionar os problemas corretamente.

var https = require('follow-redirects').https;
var fs = require('fs');

var options = {
  'method': 'GET',
  'hostname': '<HOSTNAME>',
  'path': '/services/data/v49.0/sobjects/ContentVersion/<UUID>/VersionData',
  'headers': {
    'Authorization': 'Bearer <TOKEN>',
    'Cookie': 'BrowserId=<COOKIE>',
    'Content-Type': 'text/plain'
  },
  'maxRedirects': 20
};

var req = https.request(options, function (res) {
  var chunks = [];

  res.on("data", function (chunk) {
    chunks.push(chunk);
  });

  res.on("end", function (chunk) {
    var body = Buffer.concat(chunks);
    fs.writeFileSync('output.jpg',body,'binary');
  });

  res.on("error", function (error) {
    console.error(error);
  });
});

req.end();

Obrigado a todos aqueles que ajudaram, vocês definitivamente me indicaram a direção certa.

Respostas

1 MaxGoldfarb Aug 24 2020 at 00:59

Aqui está um exemplo de solicitação de busca de JS que converti no Postman (é um pseudocódigo e provavelmente não funcionará). Eu uso o seguinte endpoint, /services/data/v48.0/sobjects/Attachment/<ATTACHMENT_FILE_ID>/Body MAS a resposta é binária, então você precisaria lidar com essa resposta e convertê-la para JPG

https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_blob_retrieve.htm

Você também pode tentar obter o conteúdo do arquivo com a API Connect e o seguinte endpoint (ainda resposta binária): /services/data/v{{version}}/connect/files/<FILE_ID>/contentOU criar um link de compartilhamento de arquivo público com o seguinte endpoint e usar esse link de uma forma ou de outra (solicitação PUT):/services/data/v{{version}}/connect/files/<FILE_ID>/file-shares/link

https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/connect_resources_files_content.htm

https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/connect_resources_files_shares_link.htm

var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer <ACCESS_TOKEN>");

var requestOptions = {
  method: 'GET',
  headers: myHeaders,
  redirect: 'follow'
};

fetch("https://domain.my.salesforce.com/services/data/v48.0/sobjects/Attachment/<ATTACHMENT_FILE_ID>/Body", requestOptions)
  .then(...)
sfdcfox Aug 23 2020 at 17:00

O ponto de extremidade Blob Retrieve realmente retorna o conteúdo do arquivo como um arquivo binário. Não há necessidade de decodificar o valor em base64. Veja a documentação .

O conteúdo do corpo do anexo é retornado em formato binário. Observe que o tipo de conteúdo de resposta não será JSON ou XML, pois os dados retornados são binários.

Observação: isso é para um corpo de anexo, mas também se aplica a qualquer outro conteúdo de Blob.


Aqui está um exemplo rápido que escrevi:

var jsforce = require('jsforce');
var fs = require('fs');
const conn = new jsforce.Connection();

conn.login('<username>', '<password>')
    .then(() => {
        conn.request('/services/data/v49.0/sobjects/ContentVersion/.../VersionData').then((result) => {
            fs.writeFileSync('output.data', result);
        });
    });