Re: Beginners: File uploads and rendering images to the database

coolbox wrote:

Hello,

I was wondering if you could offer any insigth into validating the size of the images uploaded.

I keep receiving an error on images that are around 1mb in size:

Mysql::Error: #08S01Got a packet bigger than 'max_allowed_packet' bytes: INSERT INTO photos (`content_type`, `binary_data`, `description`, `filename`) VALUES('image/jpeg',(this is followed by all the binary data for the image)'blah', 'SaltyDogsMarchofDimes 006.jpg')



Any information regarding this error would be very helpful thank you.

Pete

That is a mysql and not a RoR error, and you can adjust the max_allowed_packet in my.cnf.

Re: Beginners: File uploads and rendering images to the database

Where abouts would i find that file?

Re: Beginners: File uploads and rendering images to the database

hi there, thanks for the great tutorial.  I'm a rails/ruby newcomer so struggle sometimes to follow the code. 

I got everything working, then tried to get my list page to show the pictures as well.  For that, i simply copied across the chunk of code from show.rhtml (that checks whether to display as text or image) into list.rhtml, so my list.rhtml now has this:

<% for photo in @photos %>
  <tr>
  <% for column in Photo.content_columns %>
    <% if column.name == "binary_data" %>
      <%= image_tag("/photo_admin/code_image/#{@photo.id}", :alt => "Image") %>
    <% else %> 
      <%=h @photo.send(column.name) %>
    <% end %>
  <% end %>
  <td><%= link_to 'Show', :action => 'show', :id => photo %></td>
  <td><%= link_to 'Edit', :action => 'edit', :id => photo %></td>
  <td><%= link_to 'Destroy', { :action => 'destroy', :id => photo }, :confirm => 'Are you sure?', :method => :post %></td>
  </tr>
<% end %>

However, now when i load the list page, i get this error "You have a nil object when you didn't expect it!  The error occurred while evaluating nil.description", caused by the @photo.send line in the else block, above.

I have the feeling that i shouldn't just be copying over lumps of code anyway, since it goes against rails' DRY principles. smile  Can you (or anyone) help please?
Thanks!

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: Beginners: File uploads and rendering images to the database

Hi Mr.Dizzy

I did everything you proposed except the way I created the table. Instead of migrations I used plain mysql as given below.



create table photos (
id int not null auto_increment,
description varchar(100) not null,
content_type varchar(100) not null,
filename varchar(100) not null,
binary_data binary,
primary key (id)
);



inspite of all this, I get the gibberish in list under the binary_data filed and text 'Image' in show. The image is not getting displayed

What could I be missing?

Cheers
Cass

Re: Beginners: File uploads and rendering images to the database

Cass:

Instead of making the datatype for binary_data binary, try making it blob (or longblob..since blob has a max limit of 64k it cuts off most of my pictures)

Not sure if that will make a huge difference but that is what is created when you create the DB structure using rake

Re: Beginners: File uploads and rendering images to the database

Hi ! Newbie here..trying to acheive what Max wanted....anyone can help ? to display the image on the /list.rhtml as well...

thks alot !

Max Williams wrote:

hi there, thanks for the great tutorial.  I'm a rails/ruby newcomer so struggle sometimes to follow the code. 

I got everything working, then tried to get my list page to show the pictures as well.  For that, i simply copied across the chunk of code from show.rhtml (that checks whether to display as text or image) into list.rhtml, so my list.rhtml now has this:

<% for photo in @photos %>
  <tr>
  <% for column in Photo.content_columns %>
    <% if column.name == "binary_data" %>
      <%= image_tag("/photo_admin/code_image/#{@photo.id}", :alt => "Image") %>
    <% else %> 
      <%=h @photo.send(column.name) %>
    <% end %>
  <% end %>
  <td><%= link_to 'Show', :action => 'show', :id => photo %></td>
  <td><%= link_to 'Edit', :action => 'edit', :id => photo %></td>
  <td><%= link_to 'Destroy', { :action => 'destroy', :id => photo }, :confirm => 'Are you sure?', :method => :post %></td>
  </tr>
<% end %>

However, now when i load the list page, i get this error "You have a nil object when you didn't expect it!  The error occurred while evaluating nil.description", caused by the @photo.send line in the else block, above.

I have the feeling that i shouldn't just be copying over lumps of code anyway, since it goes against rails' DRY principles. smile  Can you (or anyone) help please?
Thanks!

Re: Beginners: File uploads and rendering images to the database

@coolbox: You can specify the size of the 'binary_data' column like this:

t.column :binary_data, :binary, :limit => 1.megabyte

The defaut MySQL blob size is 64 kilobytes.

Edit: Actually, I tried changing the field type options like this with a migration, and it doesn't work for some reason.

Update: It turns out that even though it doesn't show in the schema, the limit has been changed for the column. Also, I think this is right: TINYBLOB = 64k, BLOB = 128k, MEDIUMBLOB = 256k, LONGBLOB = 512k. At the top of pg 502 in AWDR it says you can change blob size to whatever you want, but that has not been my experience (using a :binary column type). The max it will go is 512k. Anyone know how to increase the size to, say 1.megabyte?

Last edited by berkelep (2007-07-27 22:01:36)

Re: Beginners: File uploads and rendering images to the database

Those people trying to get this to work in a list:

I managed to get it to work by removing the '@' from the Photo.id, so:

<%= image_tag("/photo_admin/code_image/#{@photo.id}", :alt => "Image") %>

changed to:

<%= image_tag("/photo_admin/code_image/#{photo.id}", :alt => "Image") %>

Last edited by mike.rowlands (2007-08-17 05:40:06)

Re: Beginners: File uploads and rendering images to the database

Great tutorial, thank you!

Re: Beginners: File uploads and rendering images to the database

This is a brilliant tutorial that's helped me learn quite a bit of Ruby, but like others, I'm having trouble getting the images to display on the list.rhtml page.

mike.rowlands wrote:

Those people trying to get this to work in a list:

I managed to get it to work by removing the '@' from the Photo.id, so:

<%= image_tag("/photo_admin/code_image/#{@photo.id}", :alt => "Image") %>

changed to:

<%= image_tag("/photo_admin/code_image/#{photo.id}", :alt => "Image") %>

This didn't work for me.

Here's my list.rhtml file. I'm certain it's something simple that's stumping me:

<h1>Listing photos</h1>

<table>
  <tr>
  <% for column in Photo.content_columns %>
    <th><%= column.human_name %></th>
   
  <% end %>
  </tr>
 
<% for photo in @photos %>
  <tr>
  <% for column in Photo.content_columns %>
    <% if column.name == "binary_data" %>
      <%= image_tag("/photo_admin/code_image/#{@Photo.id}", :alt => "Image") %>
    <% else %> 
      <%=h @Photo.send(column.name) %>
    <% end %>
  <% end %>
  <td><%= link_to 'Show', :action => 'show', :id => photo %></td>
  <td><%= link_to 'Edit', :action => 'edit', :id => photo %></td>
  <td><%= link_to 'Destroy', { :action => 'destroy', :id => photo }, :confirm => 'Are you sure?', :method => :post %></td>
  </tr>
<% end %>

</table>

<%= link_to 'Previous page', { :page => @photo_pages.current.previous } if @photo_pages.current.previous %>
<%= link_to 'Next page', { :page => @photo_pages.current.next } if @photo_pages.current.next %>

<br />

<%= link_to 'New photo', :action => 'new' %>


Edit to clarify, I've tried with both the @ sign removed and included. Neither work. Help would be greatly appreciated!

Last edited by Cosmonaut (2007-08-29 03:26:35)

Re: Beginners: File uploads and rendering images to the database

Hi Cosmonaut and others,

Let's look at the problem:

<% for photo in @photos %> 
<tr>
  <% for column in Photo.content_columns %>
  <% if column.name == "binary_data" %>
   <%= image_tag("/photo_admin/code_image/#{@Photo.id}", :alt => "Image") %>
  <% else %> 
   <%=h @Photo.send(column.name) %>
  <% end %>
<% end %>
...

In this routine, we're looping through each photo record using the first line. Remember, @photos contains our list of photos records (an array of Photo objects if we want to be specific), and we want to loop through each of them and store the current record into the photo variable so we can access it.

<% for photo in @photos %>

Therefore, when we want to get the id of the record, we need to use "photo" as our variable name. Not @photo, or Photo:

-- Photo (capitalized, and never with an @), is the class name and references the class

-- @photo (always lowercase, with an @ sign) is the instance variable. We set this in our controller. Instance variables are available in the view (our .rhtml files)

-- photo (always lowercase, without an @ sign) is a standard variable. That's the one we need, because we assigned the current photo record's data into it at the beginning of the loop (<%= for photo in @photos %>)

So now when we need to get the photo record's id, we use photo.id:

<%= image_tag("/photo_admin/code_image/#{photo.id}", :alt => "Image") %>

Remember @photos is an array of Photo objects, each object representing a photo record and all of its data. We set this in the controller. Photo (capitalized) is the class name, and we don't usually reference that. We use it here to ask it about the columns it has (description, binary etc).

Hope this clarifies things a bit.

It's probably a little confusing using Photo, @photos and photo. It might be explained better if you think of:

<% for current_record in @photos %>

The "current_record" variable is then used to hold the current photo record. This is probably a bit clearer, although it's kind of convention to do it the other way, although more confusing for the beginner!

Last edited by mr_dizzy (2007-09-01 13:41:41)

http://dizzy.co.uk/portfolio/show - Making business beautiful.

Re: Beginners: File uploads and rendering images to the database

Good stuff guys, thanks for the tutorial.  I successfully integrated the upload action into a user model for a project I'm working on, and am happy with the results.  Now on to the rendering, but I'm not too worried about that :-)

I do have a question, however.  Where does Ruby get information about the file, its filetype, and its original filename - the browser?  I see this being a problem if Ruby relies on the browser for this information, as an attacker could tweak the information to SAY that something's a picture (or a PDF, Word document, etc.)

Also, if it's the browser that gives it that information, how would you "police" the content type?  For example, I don't want to allow any content that isn't PDF, RTF or Word.  I'd have to examine the content-type in Ruby (if obj.content_type == "PDF").  But, if the browser supplies the string defining the content type, then we could have a different string for each kind of browser that's sending the information.  Testing on Safari on OS X shows me that my PDFs are being uploaded as type, "application/pdf".  That seems like a valid MIME type, but as we know, MSIE doesn't always follow the rules.

What are your suggestions/thoughts on this?  Thanks again for a great tutorial smile

Re: Beginners: File uploads and rendering images to the database

Hello, I got the following error when I tried to execute the first statement in script/console of RadRails
>> ruby script/generate model photo
ruby script/generate model photo
SyntaxError: compile error
(irb):1: syntax error, unexpected tIDENTIFIER, expecting kDO or '{' or '('
ruby script/generate model photo

Re: Beginners: File uploads and rendering images to the database

Was wondering how to modify this to allow for uploading of alternate file types - ie pdfs.

I made the change I thought necessary but after I upload the pdf, I go to:
/photo_admin/code_image/5

and I get "the file is damaged and could not be repaired".....

Is there a problem with the way I've uploaded or pulled the file since it is a different file type than specified here?

Last edited by nikikelly74 (2007-10-23 21:21:29)

Re: Beginners: File uploads and rendering images to the database

Pete, that looks like a config issue. Need to start diving into MySQL settings to extend the packet limits. The config settings is probably called "max_allowed_packet" so a search through the docs on that should give you something.

This is one of the several reasons why storing images in the database is a bad idea ...

Last edited by tobyhede (2007-10-23 18:22:18)

Toby Hede
===================================================
FiniteStateMachine - Software Development for Social Networks
===================================================

Re: Beginners: File uploads and rendering images to the database

I know this is late, but thanks to everyone who helped. My uploads are working like a dream.

Re: Beginners: File uploads and rendering images to the database

To fix the problem with .png added by the image_tag, just generate the html code yourself in the view:

<% imagetagstring = '<img alt="Image" height="160" src="/picture_controller/code_image/' %>
<% imagetagstring += @picture.id.to_s %>
<% imagetagstring += '" width="120" />' %>
<%= imagetagstring %>

That should do it, by avoiding the usage of the ruby image_tag

Re: Beginners: File uploads and rendering images to the database

This is a wonderful tutorial... and yes, I'm a newbie working my way through both the Ruby language and the Rails intricacies.

Anyways, I've worked through this content and have everything working, except...

1. I cannot get a JPG file to display.  I get the standard browser "alt" empty box.  The file data has made it into the database (checked that), and I've stepped through the code using NetBeans and found that the elements in the image/photo table are retrieved properly (filename, content type, etc.).  Below is the code in my pictures_controller.rb file for rendering:

  def code_picture
    @picture_data = Picture.find(params[:id]) 
    @picture = @picture_data.filedata 
    send_data(@picture, :type => @picture_data.content_type, :filename =>  @picture_data.filename, :disposition => 'inline')
  end

As far as I can tell, the correct data is in @picture_data as well as @picture.

Can anyone provide a direction to explore for this?

2. I still have to figure out mySQL to figure out how to make my blobs bigger.  Any help there would be helpful as well.

Thanks in advance and for a wonderful tutorial.
B.

Re: Beginners: File uploads and rendering images to the database

This is cool, I didn't see anywhere where it talks about the mysql 64k default limit. If you follow this example it will not let you upload files bigger than 64k. This is because binary maps to mysql blob type which has 64k limit. If you need bigger, consider adding the following to your migration:

execute "alter table photos modify binary_data longblob"

This will make it a long blob and able to store whatever you want, there may be other limits, but I was able to upload and view a 224k file after making this change.

Re: Beginners: File uploads and rendering images to the database

To answer several questions here, use this URL to view image instead of what tutorial says (this is easier than setting up routes, which you can do also)

/controller/code_image?id=1  (where 1 is the id of your record)