2015-12-08 5 views
0

J'ai une fonction dans mon code qui prend une chaîne représentant l'URL d'une image et crée un objet File à partir de cette chaîne, à joindre à un Tweet. Cela semble fonctionner environ 90% du temps, mais échoue parfois.Fichier Ruby IO: Impossible d'ouvrir l'URL en tant qu'objet Fichier

require 'open-uri' 
attachment_url = "https://s3.amazonaws.com/FirmPlay/photos/images/000/002/443/medium/applying_too_many_jobs_-_daniel.jpg?1448392757" 
image = File.new(open(attachment_url)) 

Si je lance le code ci-dessus, il retourne TypeError: no implicit conversion of StringIO into String. Si je change open(attachment_url) en open(attachment_url).read, j'obtiens ArgumentError: string contains null byte. J'ai également essayé d'enlever les octets nuls du fichier comme ça, mais cela ne faisait aucune différence. Maintenant, si j'essaie le code original avec une image différente, comme celle ci-dessous, cela fonctionne très bien. Il retourne un objet File comme prévu:

attachment_url = "https://s3.amazonaws.com/FirmPlay/photos/images/000/002/157/medium/mike_4.jpg" 

Je pensais qu'il avait peut-être quelque chose à voir avec les params dans l'URL d'origine, donc je dépouillé ceux, mais il n'a fait aucune différence. Si j'ouvre les images dans Chrome, elles semblent aller bien.

Je ne suis pas sûr de ce qui me manque ici. Comment puis-je résoudre ce problème?

Merci!

Mise à jour

Voici le code de travail que j'ai dans mon application:

filename = self.attachment_url.split(/[\/]/)[-1].split('?')[0] 
stream = open(self.attachment_url) 
image = File.open(filename, 'w+b') do |file| 
    stream.respond_to?(:read) ? IO.copy_stream(stream, file) : file.write(stream) 
    open(file) 
end 

La réponse de Jordan fonctionne, sauf que l'appel File.new retourne un objet File vide, alors que File.open retourne un objet File contenant le données d'image de stream.

+0

Non clair à partir de votre description: Est-ce que * * * échoue toujours pour certains fichiers jpg et * toujours * réussit pour certains autres fichiers, ou est-ce que pour un URI donné, il échoue parfois et réussit parfois? – user1934428

+1

Il échoue toujours pour certains fichiers mais jamais pour d'autres. Par exemple, le premier lien de fichier échouera toujours, tandis que le second réussira toujours. Je soupçonne qu'il peut y avoir un problème avec le fichier lui-même, donc je me demande comment détecter et contourner cela. – ACIDSTEALTH

+1

S'il s'agit d'un fichier binaire (comme un fichier JPEG), vous ne voulez pas supprimer les octets nuls. Ceux-ci font partie des données d'image. –

Répondre

2

La raison pour laquelle vous obtenez TypeError: no implicit conversion of StringIO into String est que open renvoie parfois un objet String et renvoie parfois un objet StringIO, ce qui est regrettable et déroutant. Ce qu'il fait dépend de la taille du fichier. Voir cette réponse pour plus d'informations: open-uri returning ASCII-8BIT from webpage encoded in iso-8859 (Bien que je ne recommande pas d'utiliser la gemme d'encodage garantie mentionnée ici, car elle n'a pas été mise à jour depuis 2010 et Ruby a eu des changements significatifs liés à l'encodage depuis.)

la raison pour laquelle vous obtenez ArgumentError: string contains null byte est que vous essayez de transmettre les données d'image que le premier argument de File.new:

image = File.new(open(attachment_url)) 

le premier argument de File.new doit être un nom de fichier et octets nuls ne sont pas autorisés dans noms de fichiers sur la plupart des systèmes. Essayez ceci:

image_data = open(attachment_url) 

filename = 'some-filename.jpg' 

File.new(filename, 'wb') do |file| 
    if image_data.respond_to?(:read) 
    IO.copy_stream(image_data, file) 
    else 
    file.write(image_data) 
    end 
end 

L'ouvre au-dessus du fichier (la création si elle n'existe pas, le b en 'wb' dit Ruby que vous allez écrire des données binaires), puis écrit les données de image_data à En utilisant IO.copy_stream s'il s'agit d'un objet StreamIO ou File#write sinon, il ferme à nouveau le fichier.

+0

Merci pour la solution et aussi pour clarifier la différence entre les deux méthodes d'écriture dans un fichier. J'ai l'impression d'avoir une meilleure compréhension de File/IO dans Ruby maintenant. – ACIDSTEALTH

+0

Eh bien, je pensais que cela fonctionnait, mais une fois que je l'ai branché sur mon code, il a cassé à nouveau. Maintenant, je reçois 'IOError: pas ouvert pour la lecture' de manière cohérente avec chaque image. – ACIDSTEALTH

+0

Aussi quand j'appelle 'image.size' renvoie 0'. – ACIDSTEALTH

0

Si vous utilisez Paperclip, ils ont une méthode à copier sur le disque.

def raw_image_data 
    attachment.copy_to_local_file.read 
end 

changez de pièce jointe quelle que soit la variable que vous avez utilisée bien sûr.