Персональный блог Алексея Александрова

Организация хранения файлов в PostgreSQL

  •  
January 23, 2010 03:384 коммент.

Мы живем в мире Web 2.0, где контент создают пользователи. Современное веб-приложение трудно представить без системы, позволяющей в том или ином виде загружать пользователям файлы на сервер. Далее мне хотелось бы показать, как можно организовать работу с файлами в Rails, используя возможности СУБД PostgreSQL.

 

В PostgreSQL иммется возможность хранения больших файлов, которая дает возможность потокового доступа к данным, хранящимся в специальной структуре, предназначенной для хранения "больших" объектов (PGLarge). Подробнее, о том как работать с такими объектами, можно прочесть в документации к PostgreSQL: http://www.postgresql.org/docs/8.1/static/largeobjects.html

 

Далее, я продемонстрирую код, который позволяет работать с описанными выше объектами в Rails. В первую очередь, важно учтонить, что будет использоваться адаптер 'postgres', потому что многие другие распространенные сегодня адаптеры для работой с СУБД PostgreSQL не имели возможности работать с PGLarge.

Сгенерим модель Lofile, которая и будет отвественна за хранение загружаемых файлов:

ruby script/generate model Lofile filename:string content_type:string loid:integer

Дополним модель методами, которые позволять манипулировать PGLarge объектами:

def lofile=(lofile_field)

   return if lofile_field.blank? or (buf=lofile_field.read).blank?

   self.filename = base_part_of(lofile_field.original_filename)

   self.content_type = lofile_field.content_type.chomp

   self.connection.execute("BEGIN")

   begin

      lo = self.connection.raw_connection.lo_create()

      lo.open(PGlarge::INV_WRITE)

      self.loid = lo.oid lo.write(buf)

      lo.close

   rescue PGError => e

      self.connection.execute("ROLLBACK")

      self.loid = nil

   end

   self.connection.execute("END")

end

def lofile

   ""

end

def data

   connection.raw_connection.exec("BEGIN")

   lo = connection.raw_connection.lo_open(loid, PGlarge::INV_READ)

   content = lo.read

   lo.close

   connection.raw_connection.exec("END")

   content

end

def data=(text)

   connection.raw_connection.exec("BEGIN")

   lo = connection.raw_connection.lo_open(loid, PGlarge::INV_WRITE)

   content = lo.write(text)

   lo.close

   connection.raw_connection.exec("END")

   content

end

def base_part_of(file_name)

   name = File.basename(file_name)

   name.gsub(/[^\w._-]/, '')

end

Таким образом, с помощью методов data и data= получаем и устанавливаем соотвественно те данные, которые загружает пользовательа, lofile и lofile= достает эти данные из HTTP-запроса. То есть надо создать во view нечто подобное:

<% form_for @lofile, :url=>{...}, :html=>{:multipart=>true} do |f| %> <%= f.error_messages %>

<%= f.label :lofile%> <%= f.file_field :lofile, :size => 80 %

<%= f.submit "Загрузить" %>

<% end %>

 

Остался контроллер:

def create

   @lofile = Lofile.new(params[:lofile])

   if @lofile.save

      flash[:notice]="Файл успешно загружен"

      redirect_to ...

   else

     render :action => 'new'

   end

end

 

Ну а отдавать пользователю файл тоже очень просто. К примеру, так:

send_data(@lofile.data, :filename => @lofile.name, :type => @lofile.content_type, :disposition => "inline")

, , ,
А на сколько эти файлы могут быть большими???
Нашел, пишут что около 2 гигов
Используется адаптер pg. По описанию он новее и поддерживается активно ActiveRecord: , будет работать? http://wiki.rubyonrails.org/database-support/postgres
Периодически на сайте rubyonrails.org меняется название рекомендуемого адаптера. Я помню сначала был pg, потом postgres, сейчас вот они оба, но больше pg. Я не использую pg в своих проектах, поскольку помню, что с ним были проблемы. Если критично использовать pg, и будут проблемы, то всегда в модели можно написать require 'postgres'
Зарегистрируйтесь для добавления сообщений