Carregando uma imagem local usando a API

E aí, pessoal,

Espero que este seja o lugar certo para perguntar. Estou trabalhando na importação de um conjunto de dados de outro fórum para nossa nova instância do Discourse (postos de conhecimento interno que queremos migrar).
Tenho nossos dados antigos bem organizados em várias estruturas JSON, mas estou travado ao fazer o upload das várias imagens relacionadas aos posts antigos.

Estou usando o gem discourse_api e tenho tido ótimos resultados até agora, embora eu saiba que ele é incompleto. Consigo fazer o upload de uma imagem com sucesso se essa imagem estiver hospedada em algum lugar, assim:

client.upload_file({'user_id':24,url:'http://valid.url.here/foo.jpg','synchronous':true})

mas estou sem saída ao tentar fazer o upload de uma imagem que está localizada no meu computador:

client.upload_file({'user_id':24,file:'/local/path/to/image/foo.jpg','synchronous':true})

…o acima retorna:

DiscourseApi::UnprocessableEntity: {"failed"=>"FAILED", 
"message"=>"undefined method 'tempfile' for 
#<String:0x00007f409d2fa398>"} from /home/user/.rvm/gems/
ruby-2.3.7/gems/discourse_api-0.36.0/lib/discourse_api/
client.rb:154:in 'handle_error'

Já pesquisei o suficiente para perceber que provavelmente preciso fazer alguma mágica com base64 ou algo assim para que minha requisição esteja corretamente formatada (também estou percebendo que talvez o método de upload do gem Ruby não consiga lidar com esse tipo de upload (e se for o caso, tudo bem).

Principalmente, estou apenas procurando orientação, para ver se mais alguém já passou por isso e para saber se estou seguindo o caminho errado. Obrigado antecipadamente e tudo mais :slight_smile:

A menos que haja algum motivo que o impeça, recomendo verificar o diretório do script de importação para ver os importadores que usam arquivos JSON. Será mais fácil do que a API.

Olá Tony,

Concordo que fazer upload de arquivos locais pode ser um pouco complicado de entender usando a API. Isso deve resolver:

# upload_image.rb
require 'discourse_api'
require 'fileutils'

client = DiscourseApi::Client.new("URL do Site Discourse")
client.api_key = "Sua chave de API"
client.api_username = "Nome de usuário"

filename = ARGV[1] # caminho completo do arquivo /home/tony/mypic.png

args = {
  :file => Faraday::UploadIO.new(filename, 'image/png')
}

resp = client.upload_file(args)

url = resp['url']
width = resp['width']
height = resp['height']



# topic_id é o ID do tópico para o qual você deseja fazer upload da imagem.
args = {
  :topic_id => ARGV[0],
  :raw => "<img src=\"#{url}\" width=\"#{width}\" height=\"#{height}\">"
}

# Isso criará um novo post no tópico especificado
resp = client.create_post(args)

Isso usará a gem Faraday para fornecer os requisitos de arquivo corretos para upload_file. Assim que o arquivo for carregado, ele precisa ser atribuído a um tópico/post para aparecer, caso contrário, será excluído automaticamente.

Você pode chamar este script com

ruby upload_image.rb <topic_id> <filename>
ruby upload_image.rb 128 /home/tony/mypic.png

Blake,

Muito obrigado. Vou ler sobre a gem Faraday para entender o que ela está fazendo por mim e testar seu script. Agradeço muito por ter tirado um tempo para isso!

  • Tony.

Gastei bastante tempo tentando descobrir como invocar o endpoint de upload a partir de um script em Python. Primeiro, segui a recomendação de fazer engenharia reversa na chamada da API de upload usando as ferramentas de desenvolvedor do Chrome. Em seguida, repliquei a chamada de upload pelo Postman. A parte que mais me tomou tempo foi traduzir isso para um código Python funcional. Por algum motivo, o código Python gerado pelo Postman também não funcionava. Foi assim que finalmente funcionou para mim:

requests.post('/uploads.json', files={'files[]': open('/path/to/image.png', 'rb')}, data={'type': 'composer'})