2012-03-18 33 views
27

Tôi biết có những câu hỏi tương tự câu hỏi này nhưng tôi chưa tìm thấy câu trả lời hay. Những gì tôi cần làm là gửi một mô tả của một đối tượng đến một trong các phương pháp tạo của tôi, trong đó bao gồm một số thuộc tính khác nhau trong đó có một tên là: hình ảnh, tập tin đính kèm kẹp giấy:Gửi tệp tới API JSON Rails

has_attached_file :image 

Bây giờ tôi đã đọc rằng việc gửi hình ảnh có thể được thực hiện thẳng trong JSON bằng cách mã hóa và giải mã hình ảnh dưới dạng base64, nhưng cảm giác đó giống như một giải pháp bẩn đối với tôi. Phải có cách tốt hơn.

Một giải pháp khác là gửi yêu cầu nhiều phần/dữ liệu biểu mẫu, giống như yêu cầu LEEjava describes here. Vấn đề với thông số đó là các tham số yêu cầu không được diễn giải chính xác trong Rails 3.2.2 và JSON.parse sẽ phát ra lỗi khi nó cố gắng phân tích các thông số, hoặc có lẽ đó là Rails giải thích sai điều gì đó.

Started POST "/api/v1/somemodel.json?token=ZoipX7yhcGfrWauoGyog" for 127.0.0.1 at 2012-03-18 15:53:30 +0200 Processing by Api::V1::SomeController#create as JSON Parameters: {"{\n
\"parentmodel\": {\n \"superparent_id\": 1,\n
\"description\": \"Enjoy the flower\",\n \"\": "=>{"\n
{\n \"someattribute\": 1,\n
\"someotherattribute\": 2,\n \"image\": \"image1\"\n
}\n "=>{"\n }\n}"=>nil}}, "token"=>"ZoipX7yhcGfrWauoGyog"}

Thật khó để đọc điều đó, xin lỗi. JSON.parse (params [: parentmodel]) là không thể ở đây, và tôi có thể không JSON.parse (params) hoặc do các thuộc tính thẻ, JSON.parse (params) throws lỗi này:

TypeError (can't convert ActiveSupport::HashWithIndifferentAccess into String)

Điều này khiến tôi tin rằng tôi đang tiếp cận vấn đề này hoàn toàn sai, hoặc tôi chỉ đang làm điều gì đó. Dù bằng cách nào, chúng tôi có thể chắc chắn rằng tôi sai về điều gì đó. :)

Có cách nào tốt hơn để thực hiện việc này không? Ai đó có thể chỉ cho tôi bất kỳ hướng dẫn/hướng dẫn nào hay viết câu trả lời mô tả cách tôi nên tiếp cận điều này?

Cảm ơn bạn trước

UPDATE: Vì vậy, tôi đã thực sự nhận nó làm việc bây giờ, nhưng chỉ trong các thử nghiệm. Tôi không hoàn toàn chắc chắn làm thế nào điều này hoạt động, nhưng có lẽ ai đó có thể điền vào những khoảng trống cho tôi? Đây là một phần của mã thử nghiệm (hình ảnh: fixture_file_upload (...) là phần quan trọng).

parts_of_the_object = { someattribute: 0, someotherattribute: 0, image: fixture_file_upload('/images/plot.jpg', 'image/jpg') } 

params của tôi [] trông giống như một hình thức HTML bình thường đã được gửi, đó là lạ (và tuyệt vời):

Parameters: {"superparentid"=>"1", "plots"=>[{"someattribute"=>"0", "someotherattribute"=>"0", "image"=>#<ActionDispatch::Http::UploadedFile:0x007f812eab00e8 @original_filename="plot.jpg", @content_type="image/jpg", @headers="Content-Disposition: form-data; name=\"plots[][image]\"; filename=\"plot.jpg\"\r\nContent-Type: image/jpg\r\nContent-Length: 51818\r\n", @tempfile=#<File:/var/folders/45/rcdbb3p50bl2rgjzqp3f0grw0000gn/T/RackMultipart20120318-1242-1cn036o>>}], "token"=>"4L5LszuXQMY6rExfifio"} 

Yêu cầu được thực hiện chỉ thích và bài yêu cầu được thực hiện với rspec:

post "/api/v1/mycontroller.json?token=#{@token}", thefull_object 

Vì vậy, tôi đã hoàn thành công việc. Tôi chỉ không biết chính xác nó hoạt động như thế nào! Tôi cũng muốn tự mình tạo ra một phản ứng như thế này, không chỉ từ RSpec. :-)

Trả lời

38

Tôi đã thực sự có một thời gian khủng khiếp với câu hỏi này ngày hôm qua để làm điều gì đó rất giống nhau. Trong thực tế, tôi đã viết câu hỏi: Base64 upload from Android/Java to RoR Carrierwave

Điều gì đã xảy ra là tạo đối tượng hình ảnh được tải lên trong bộ điều khiển và sau đó tiêm nó trở lại vào các tham số. Đối với ví dụ cụ thể này, chúng tôi đang lấy một tệp base64 (mà tôi cho rằng bạn có, vì JSON không hỗ trợ tệp được nhúng) và lưu nó dưới dạng tệp tạm thời trong hệ thống, sau đó chúng tôi đang tạo đối tượng UploadedFile đó và cuối cùng reinjecting nó vào params.

gì json/my params trông giống như:

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

Đây là những gì điều khiển của tôi trông giống như bây giờ:

# POST /pictures 
    # POST /pictures.json 
    def create 

    #check if file is within picture_path 
    if params[:picture][:picture_path]["file"] 
     picture_path_params = params[:picture][:picture_path] 
     #create a new tempfile named fileupload 
     tempfile = Tempfile.new("fileupload") 
     tempfile.binmode 
     #get the file and decode it with base64 then write it to the tempfile 
     tempfile.write(Base64.decode64(picture_path_params["file"])) 

     #create a new uploaded file 
     uploaded_file = ActionDispatch::Http::UploadedFile.new(:tempfile => tempfile, :filename => picture_path_params["filename"], :original_filename => picture_path_params["original_filename"]) 

     #replace picture_path with the new uploaded file 
     params[:picture][:picture_path] = uploaded_file 

    end 

    @picture = Picture.new(params[:picture]) 

    respond_to do |format| 
     if @picture.save 
     format.html { redirect_to @picture, notice: 'Picture was successfully created.' } 
     format.json { render json: @picture, status: :created, location: @picture } 
     else 
     format.html { render action: "new" } 
     format.json { render json: @picture.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

Điều duy nhất còn lại để làm vào thời điểm này là để xóa các tempfile, mà tôi tin rằng có thể được thực hiện với tempfile.delete

Tôi hy vọng điều này sẽ giúp với câu hỏi của bạn! Tôi đã dành cả ngày để tìm một giải pháp ngày hôm qua, và mọi thứ tôi đã thấy là một kết thúc chết. Điều này, tuy nhiên, hoạt động trên các trường hợp thử nghiệm của tôi.

+0

Hãy cho tôi biết nếu nó không hoạt động trong chế độ dev/prod của bạn, tôi đã tìm ra giải pháp cho điều này vài ngày trước nhưng đã quên ot để viết một câu trả lời. Nó khá giống với của bạn, chỉ tôi đang làm nó trong mô hình (gửi dữ liệu Base64 ngay vào một thuộc tính image_data). Tôi thích giải pháp của bạn hơn. Hãy cho tôi biết làm thế nào nó đi và tôi sẽ giúp đỡ nếu tôi có thể. –

+0

Từ những gì tôi có thể nhìn thấy câu trả lời là chính xác vì vậy tôi đang trao tiền thưởng. –

+0

Đây là một câu trả lời hay, nhưng ít nhất trong Rails 3/Ruby 1.9 có một số lỗ nhỏ. Trước tiên, đảm bảo bạn 'tempfile.rewind()' sau khi bạn viết, nếu không bạn sẽ nhận được tệp có độ dài 0. – Shannon

8

TomJ đưa ra một câu trả lời hay, nhưng ít nhất trong Rails 3/Ruby 1.9 có một số lỗ nhỏ.

Trước tiên, đừng cố gắng gọi [] về những gì có thể là đối tượng UploadedFile trong đối tượng thông số của bạn. Hãy chắc chắn rằng bạn kiểm tra xem nó .is_a?(Hash) đầu tiên, ví dụ.

Ngoài ra, hãy đảm bảo rằng bạn tempfile.rewind() sau khi bạn viết, nếu không bạn sẽ nhận được tệp có độ dài 0.

Khoá :original_filename trong tham số cho hàm tạo của UploadedFile là không cần thiết/không sử dụng. Mặt khác, bạn có thể muốn cung cấp khóa :type. Một cách dễ dàng để tìm giá trị cho loại là mime_type = Mime::Type.lookup_by_extension(File.extname(original_filename)[1..-1]).to_s

Đây là một phiên bản với những thay đổi áp dụng:

# POST /pictures 
# POST /pictures.json 
def create 

    #check if file is within picture_path 
    if params[:picture][:picture_path].is_a?(Hash) 
    picture_path_params = params[:picture][:picture_path] 
    #create a new tempfile named fileupload 
    tempfile = Tempfile.new("fileupload") 
    tempfile.binmode 
    #get the file and decode it with base64 then write it to the tempfile 
    tempfile.write(Base64.decode64(picture_path_params["file"])) 
    tempfile.rewind() 

    mime_type = Mime::Type.lookup_by_extension(File.extname(original_filename)[1..-1]).to_s 
    #create a new uploaded file 
    uploaded_file = ActionDispatch::Http::UploadedFile.new(
     :tempfile => tempfile, 
     :filename => picture_path_params["filename"], 
     :type => mime_type) 

    #replace picture_path with the new uploaded file 
    params[:picture][:picture_path] = uploaded_file 
    end 

    @picture = Picture.new(params[:picture]) 
    respond_to do |format| 
    if @picture.save 
     format.html { redirect_to @picture, notice: 'Picture was successfully created.' } 
     format.json { render json: @picture, status: :created, location: @picture } 
    else 
    format.html { render action: "new" } 
    format.json { render json: @picture.errors, status: :unprocessable_entity } 
    end 
end 

cuối

+0

{: avatar => ["Kẹp giấy :: Lỗi :: NotIdentifiedByImageMagickError", "Paperclip :: Errors :: NotIdentifiedByImageMagickError"]}> Ý tưởng nào? – ajbraus

Các vấn đề liên quan