Задача:

У нас есть более 1500 файлов .msg в Salesforce, которые представляют собой контейнеры Outlook для почтовых бесед и вложений. Идея состоит в том, чтобы создать сценарий, который загружает все файлы, извлекает каждое вложение и почтовую переписку и загружает файлы в Salesforce с соответствующими возможностями. На данный момент нам удобно использовать JSForce. Запрос всех файлов в базе данных работает нормально. Но VersionData — это путь к фактическому документу, а не к самому документу.

Проблема:

Вот важная часть кода:

conn.queryAll(oneLine`
  SELECT ContentDocumentId,
    ContentDocument.Title,
    ContentDocument.FileExtension,
    ContentDocument.LatestPublishedVersion.VersionData,
    ContentDocument.LatestPublishedVersion.Id
  FROM ContentDocumentLink
  WHERE ContentDocument.FileExtension = 'msg' AND
    LinkedEntityId = '${teamId}'
`)
  .then(result => {
    result.records.map(document => {
      if (!document.ContentDocument.LatestPublishedVersion) {
        /* error handling */
      }
      const filename =
        document.ContentDocumentId + '.' +
        document.ContentDocument.FileExtension

      const dataPath = document.ContentDocument.LatestPublishedVersion.VersionData
      conn.request(dataPath)
        .then(data => {
          fs.writeFile(`./files/${filename}`, data, () => {
            console.log(`downloaded ${filename}`)
            /* Do stuff */
          })
        })
        .catch(requestError => {
          fs.appendFile(logNames.requestErrors, `${requestError}\n\n\n\n\n`,() => {})
        })
    })
...

Он успешно загружает файлы, но они кажутся поврежденными, потому что ни один из них не может быть просмотрен любой программой, подходящей для открытия файлов .msg.

У меня был другой подход:

...
  .then(result => {
    result.records.map(document => {
      if (!document.ContentDocument.LatestPublishedVersion) {
        /* error handling */
      }
      const filename =
        document.ContentDocumentId + '.' +
        document.ContentDocument.FileExtension

      const fileDesc = fs.createWriteStream(`./files/${filename}`)
        .on('error' , error => {
          console.error(error)
        })
      conn
        .sobject('ContentVersion')
        .record(document.ContentDocument.LatestPublishedVersion.Id)
        .blob('VersionData')
        .pipe(fileDesc)
        .on('error', error => {
          console.error(error)
        })
        .end(() => {
          console.log('Closing FD')
          fileDesc.close()
        })
    })

Здесь ничего не произошло. Ничего не загружалось, никаких ошибок не печаталось и не регистрировалось.

Документация кажется устаревшей или, по крайней мере, бесполезной для меня. Я ценю любую помощь, которую я могу получить.

1
Eleos91 10 Мар 2020 в 19:22
Да, это так, но заставить работать настоящие HTTP-запросы было довольно сложно. Большое тебе спасибо!
 – 
Eleos91
11 Мар 2020 в 13:39

1 ответ

Лучший ответ

С помощью полезной ссылки из identigral я заставил ее работать. С токеном, сгенерированным jsfroce и использующим node-fetch, это совсем не сложно.

Итак, мои решения для загрузки пакета документов VersionData следующие:

const jsforce = require('jsforce')
const { oneLine } = require('common-tags')
const fs = require('fs')
const fetch = require("node-fetch")

conn.queryAll(oneLine`
  SELECT ContentDocumentId,
    ContentDocument.FileExtension,
    ContentDocument.LatestPublishedVersion.VersionData,
    ContentDocument.LatestPublishedVersion.Id
  FROM ContentDocumentLink
  WHERE ContentDocument.FileExtension = 'msg' AND
    LinkedEntityId = '${teamId}'
`)
  .then(result => {
    console.log(result.records.length)
    result.records.map(document => {
      if (!document.ContentDocument.LatestPublishedVersion) {
        /* error handling */
        return
      }
      const filename =
        document.ContentDocumentId + '.' +
        document.ContentDocument.FileExtension

      const dataPath = document.ContentDocument.LatestPublishedVersion.VersionData
      headers = {
        'Authorization': 'Bearer ' + conn.accessToken,
        'Content-Type': 'blob',
      }
      options = {
        method: 'GET',
        headers: headers,
      }
      fetch(conn.instanceUrl + dataPath, options)
        .then(result => {
          result.body
            .pipe(fs.createWriteStream(`./files/${filename}`))
        })
        .catch(error => {
          console.log(error)
        })
    })

...

Похоже, что в запросе jsforce есть ошибка или что-то в этом роде. Возможно, проблема заключается в запросе большого двоичного объекта и получении строки. Я пытаюсь открыть тему. Я надеюсь, что у некоторых людей не будет такой же проблемы, как у меня.

0
Eleos91 11 Мар 2020 в 13:55