Cargando una imagen local usando la API

Hola a todos,

Espero que este sea el lugar correcto para preguntar. Estoy trabajando en importar un conjunto de datos desde otro foro a nuestra nueva instancia de Discourse (publicaciones internas de conocimiento que queremos migrar).

Tengo nuestros datos antiguos bien organizados en varias estructuras JSON, pero me he atascado al subir las diversas imágenes relacionadas con los antiguos mensajes.

Estoy utilizando el gem discourse_api y hasta ahora he tenido un éxito notable, aunque sé que está incompleto. Puedo subir una imagen sin problemas si esta está alojada en algún lugar, de la siguiente manera:

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

Sin embargo, me encuentro bloqueado al intentar subir una imagen que se encuentra en mi máquina local:

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

…lo anterior devuelve el siguiente error:

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'

He investigado lo suficiente como para darme cuenta de que probablemente necesite realizar algún truco con base64 o algo similar para que mi solicitud esté correctamente formada (también me doy cuenta de que quizás el método de subida del gem de Ruby simplemente no pueda manejar este tipo de carga, y si es así, no hay problema).

Principalmente, solo busco orientación, para ver si alguien más se ha encontrado con esto y para saber si estoy siguiendo el camino equivocado. Gracias de antemano, y todo eso :slight_smile:

A menos que haya alguna razón que te impida hacerlo, te recomiendo que consultes el directorio del script de importación para ver los importadores que utilizan archivos JSON. Será más fácil que usar la API.

Hola Tony,

Estoy de acuerdo en que subir archivos locales puede ser un poco complicado de entender usando la API. Esto debería funcionar:

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

client = DiscourseApi::Client.new("URL del sitio Discourse")
client.api_key = "Tu clave de API"
client.api_username = "Nombre de usuario"

filename = ARGV[1] # ruta completa al archivo /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 es el identificador del tema al que quieres subir la imagen.
args = {
  :topic_id => ARGV[0],
  :raw => "<img src=\"#{url}\" width=\"#{width}\" height=\"#{height}\">"
}

# Esto creará un nuevo post en el tema especificado
resp = client.create_post(args)

Esto utilizará la gem Faraday para proporcionar los requisitos de archivo correctos para upload_file. Una vez que el archivo se haya subido, debe asignarse a un tema o publicación para que se muestre; de lo contrario, se eliminará automáticamente.

Puedes ejecutar este script con:

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

Blake,

¡Muchas gracias! Voy a leer sobre la gema Faraday para entender qué hace por mí y probar tu script. ¡Realmente agradezco que te hayas tomado el tiempo!

-tony.

Pasé bastante tiempo tratando de averiguar cómo invocar el endpoint de carga desde un script de Python. Primero seguí la recomendación de realizar ingeniería inversa a la llamada de la API de carga mediante las herramientas de desarrollo de Chrome. Luego repliqué la llamada de carga a través de Postman. La parte que más me llevó tiempo fue traducir eso a un código de Python funcional. Por alguna razón, el código de Python generado por Postman tampoco funcionaba. Así es como finalmente funcionó para mí:

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