2012-03-24 6 views
7

Ich habe die Lösung von use base64 image with Carrierwave hinzugefügt, um ein Bild aus einer Java-Klasse hochladen. Dies ist jetzt, was meine Fileuploader Klasse aussieht - und ich glaube, wo das Problem ist:Base64-Upload von Android/Java zu RoR Carrierwave

# encoding: utf-8 

class FileUploader < CarrierWave::Uploader::Base 

    # Include RMagick or MiniMagick support: 
    include CarrierWave::RMagick 
    # include CarrierWave::MiniMagick 

    # Choose what kind of storage to use for this uploader: 
    storage :file 
    # storage :fog 

    #START FROM BASE64 POST LINKED ABOVE 
    class FilelessIO < StringIO 
    attr_accessor :original_filename 
    attr_accessor :content_type 
    end 

    before :cache, :convert_base64 

    def convert_base64(file) 
    if file.respond_to?(:original_filename) && 
     file.original_filename.match(/^base64:/) 
     fname = file.original_filename.gsub(/^base64:/, '') 
     ctype = file.content_type 
     decoded = Base64.decode64(file.read) 
     file.file.tempfile.close! 
     decoded = FilelessIO.new(decoded) 
     decoded.original_filename = fname 
     decoded.content_type = ctype 
     file.__send__ :file=, decoded 
    end 
    file 
    end 
#END FROM POST LINKED ABOVE 


    # Override the directory where uploaded files will be stored. 
    # This is a sensible default for uploaders that are meant to be mounted: 
    def store_dir 
    "uploads/#{model.class.to_s.underscore}/#{model.user_id}" 
    end 

    # Provide a default URL as a default if there hasn't been a file uploaded: 
    # def default_url 
    # "/images/fallback/" + [version_name, "default.png"].compact.join('_') 
    # end 

    # Process files as they are uploaded: 
    # process :scale => [200, 300] 
    # 
    # def scale(width, height) 
    # # do something 
    # end 

    # Create different versions of your uploaded files: 
    version :thumb do 
     process :resize_to_fit => [200, 300] 
    end 

    version :web do 
     process :resize_to_fit => [1000, 1000] 
    end 

    # Add a white list of extensions which are allowed to be uploaded. 
    # For images you might use something like this: 
    def extension_white_list 
    %w(jpg jpeg gif png) 
    end 

    # Override the filename of the uploaded files: 
    # Avoid using model.id or version_name here, see uploader/store.rb for details. 
    def filename 
    if original_filename 
    Time.new.to_i.to_s+"_"+original_filename 
    end 
    end 

end 

Das Bild Modell:

class Picture < ActiveRecord::Base 

    belongs_to :user 
    belongs_to :folders 

    attr_accessible :user_id, :picture_name, :picture_description, 
    :folder_id, :picture_path, :file_save 

    mount_uploader :picture_path, FileUploader 

    before_save :update_pictures_attributes 

    def update_pictures_attributes 
     self.file_size = picture_path.file.size 
    end 

end 

Gerade jetzt, wenn die Post Aufruf der Dateipfad gemacht das ist in der db gespeichert ist nil - aber alles andere ist gespeichert. Hier ist die Java/Android-Klasse:

import java.io.File; 
import java.io.IOException; 
import java.util.ArrayList; 
import java.util.List; 

import org.apache.http.client.*; 
import org.apache.http.client.entity.*; 
import org.apache.http.client.methods.*; 
import org.apache.http.entity.StringEntity; 
import org.apache.http.impl.client.*; 
import org.apache.http.message.*; 
import org.apache.commons.io.FileUtils; 
import org.json.*; 
import android.util.Base64; 
import android.util.Log; 

public class Uploader { 

    private String url; 
    private String fileName; 

    public Uploader(String url, String fileName){ 
     this.url = url; 
     this.fileName = fileName; 
    } 

    public Boolean upload() throws JSONException, ClientProtocolException, IOException { 
     Boolean success = true; 
     JSONObject jsonObject = constructPictureJson(); 
      DefaultHttpClient httpClient = new DefaultHttpClient(); 

      ResponseHandler <String> responseHandler = new BasicResponseHandler(); 
      HttpPost postMethod = new HttpPost(url); 
      postMethod.setEntity(new StringEntity(jsonObject.toString())); 
      postMethod.setHeader("Accept", "application/json"); 
      postMethod.setHeader("Content-type", "application/json"); 
      postMethod.setHeader("Data-type", "json"); 
      try{ 
      httpClient.execute(postMethod, responseHandler); 
      } catch (org.apache.http.client.HttpResponseException error){ 
       Log.d("Uploader Class Error", "Error code: "+error.getStatusCode()); 
       Log.d("Uploader Class Error", "Error message: "+error.getMessage()); 
       success = false; 
      } 
      //Log.d("server resposne", response); 
      return success; 
    } 

    public JSONObject constructPictureJson() throws JSONException, IOException{ 
     String userId = "1"; 
     String folderId = "1"; 
     String[] file = fileName.split("/"); 
     JSONObject pictureData = new JSONObject(); 
     pictureData.put("user_id", userId); 
     pictureData.put("folder_id", folderId); 
     pictureData.put("picture_name", "picture name"); 
     pictureData.put("picture_description", "1"); 
     pictureData.put("content_type", "jpg"); 
     pictureData.put("original_filename", "base64:"+file[file.length-1]); 
     pictureData.put("filename", file[file.length-1]); 
     pictureData.put("picture_path", encodePicture(fileName)); 

     return pictureData; 
    } 

    public String encodePicture(String fileName) throws IOException{ 
     File picture = new File(fileName); 
     return Base64.encodeToString(FileUtils.readFileToByteArray(picture), Base64.DEFAULT); 
    } 

} 

Hat jemand irgendwelche Ideen? Ich war den ganzen Tag dran. Ich denke, weil ich nicht viel über Ruby weiß, bin ich entweder (1) Missbildung der Anfrage; oder (2) Ich habe das base64-Image mit Carrierwave falsch implementiert.

Antwort

19

Endlich das Problem gelöst! Ich hoffe, diese Antwort hilft anderen, die versuchen, dieses Problem zu lösen, da es keine gute Ressource dafür gibt. Das war überraschend, da ich dachte, andere hätten das Gleiche tun wollen. Meine ursprünglichen Änderungen an der Carrierwave-Initialisierungsdatei scheinen eine Sackgasse gewesen zu sein.

Es ging darum, das hochgeladene Bildobjekt im Controller zu erstellen und es dann wieder in die Parameter einzufügen.

Für dieses spezielle Beispiel nehmen wir eine Base64-Datei (die vermutlich JSON nicht unterstützt) und speichern sie als temporäre Datei im System, dann erstellen wir das UploadedFile-Objekt und schließlich es in die Params reinjizieren.

Was sieht mein json/params wie:

picture {:user_id => "1", :folder_id => 1, etc., :picture_path {:file => "base64 awesomeness", :original_filename => "my file name", :filename => "my file name"}} 

Hier ist, was mein Controller sieht aus wie jetzt:

40  # POST /pictures 
41 # POST /pictures.json 
42 def create 
43 
44  #check if file is within picture_path 
45  if params[:picture][:picture_path]["file"] 
46   picture_path_params = params[:picture][:picture_path] 
47   #create a new tempfile named fileupload 
48   tempfile = Tempfile.new("fileupload") 
49   tempfile.binmode 
50   #get the file and decode it with base64 then write it to the tempfile 
51   tempfile.write(Base64.decode64(picture_path_params["file"])) 
52  
53   #create a new uploaded file 
54   uploaded_file = ActionDispatch::Http::UploadedFile.new(:tempfile => tempfile, :filename => picture_path_params["filename"], :original_filename => picture_path_params["original_filename"]) 
55  
56   #replace picture_path with the new uploaded file 
57   params[:picture][:picture_path] = uploaded_file 
58  
59  end 
60 
61  @picture = Picture.new(params[:picture]) 
62 
63  respond_to do |format| 
64  if @picture.save 
65   format.html { redirect_to @picture, notice: 'Picture was successfully created.' } 
66   format.json { render json: @picture, status: :created, location: @picture } 
67  else 
68   format.html { render action: "new" } 
69   format.json { render json: @picture.errors, status: :unprocessable_entity } 
70  end 
71  end 
72 end 

Das einzige, was an dieser Stelle noch zu tun ist, um die temporären Dateien zu löschen, was ich glaube, kann mit tempfile.delete

getan werden Ich hoffe, das hilft bei Ihrer Frage! Ich habe gestern den ganzen Tag nach einer Lösung gesucht und alles, was ich gesehen habe, ist eine Sackgasse. Dies funktioniert jedoch bei meinen Testfällen.

+1

Das hat super geklappt! Danke dafür. Am Ende habe ich die params-Verarbeitung in einen Aufruf 'before_filter: process_attached_file, nur:: create' gesetzt, um die Create-Methode sauberer zu halten. –

+3

Ich versuche das Gleiche zu tun; Kannst du deinen letzten Java Code für deine Android App posten? – scientiffic

+0

Haben Sie etwas dagegen, Ihre endgültige Implementierung für die Upload-Funktion auf der Android-Seite zu teilen? Ich habe eine ähnliche Sache gemacht und ich benutze auch Rails, aber der eigentliche Upload ist unglaublich langsam - ich nehme an, dies ist, weil ich auf Webrick teste, aber ich wäre gespannt, ob Sie das Bild verkleinern oder die Auflösung verringern in irgendeiner Weise vor dem Hochladen – Riptyde4