The question is just above the last code snippet. Thank you. (environment details are the end )
posts_controller.rb
class PostsController < ApplicationController
def create
@post = Post.new(params[:post])
respond_to do |format|
format.xml { render :xml => @post.to_xml(:include => [ :assets])}
end
end
posts.rb
class Post < ActiveRecord::Base
has_many :assets, :as => :attachable, :dependent => :destroy
end
asset.rb
class Asset < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true
has_attached_file :data,
:url => "/assets/:id",
:path =>":rails_root/assets/:id_partition/:style/:basename.:extension"
def name
data_file_name
end
def content_type
data_content_type
end
def file_size
data_file_size
end
end
now when we post this information
POST /posts.xml HTTP/1.1
Accept-Encoding: gzip,deflate
Accept: application/xml
Content-Type: application/xml
User-Agent: Jakarta Commons-HttpClient/3.1
Host: localhost:8080
Content-Length: 60
<post><body>postbody</body><title>post_title</title></post>
a post entry gets created and when I post this
POST /posts.xml HTTP/1.1
Content-type: multipart/mixed; boundary=---------------------------7d226f700d0
Accept: application/xml,text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Cache-Control: no-cache
Pragma: no-cache
User-Agent: Java/1.6.0_21
Host: 192.168.0.105:8080
Connection: keep-alive
Content-Length: 1710
-----------------------------7d226f700d0
content-disposition: form-data; name="post[title]"
Content-Length: 10
post_title
-----------------------------7d226f700d0
content-disposition: form-data; name="post[body]"
Content-Length: 8
postbody
-----------------------------7d226f700d0
content-disposition: form-data; name="post[assets_attributes][0][data]"; filename="C:/Users/mv288/files/1.txt"
content-type: application/octet-stream
ÿþ
sample file content
-----------------------------7d226f700d0
content-disposition: form-data; name="post[assets_attributes][0][data]"; filename="C:/Users/mv288/Pictures/1.txt"
content-type: application/octet-stream
ÿþ
sample file content
-----------------------------7d226f700d0
a new post gets created with 2 file attachments.
now the question is, I want to get the following HTTP post ( please notice the xml part before the file attachments) to also create a post with 2 attachments, with no additional changes ( to posts_controller or routes.rb). is that possible?
POST /posts.xml HTTP/1.1
Content-type: multipart/mixed; boundary=---------------------------7d226f700d0
Accept: application/xml,text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Cache-Control: no-cache
Pragma: no-cache
User-Agent: Java/1.6.0_21
Host: 192.168.0.105:8080
Connection: keep-alive
Content-Length: 1710
-----------------------------7d226f700d0
Content-type: application/xml; charset=UTF-8
Content-Length: 59
<post><body>postbody</body><title>post_title</title></post>
-----------------------------7d226f700d0
content-disposition: form-data; name="post[assets_attributes][0][data]"; filename="C:/Users/mv288/files/1.txt"
content-type: application/octet-stream
ÿþ
sample file content
-----------------------------7d226f700d0
content-disposition: form-data; name="post[assets_attributes][0][data]"; filename="C:/Users/mv288/Pictures/1.txt"
content-type: application/octet-stream
ÿþ
sample file content
-----------------------------7d226f700d0Blockquotetest
using jruby 1.5.2/jdk1.6, rails 2.3.4, paperclip-2.3.3 on windows 2007 - 64 bit
At least rails 2.3.4 does not do this automatically. One needs to write a multipart/related parser and register it, inside initializers/mime_types.rb
NOTE : Feel free to update the hard coded values( like main and attachment part prefix etc.,) in your copy.
This same strategy can be used for multipart/related content as well. We are still debating whether to use multipart/related or multipart/mixed in this example below.
Microsoft's notes on this subject. http://msdn.microsoft.com/en-us/library/ms527355(EXCHG.10).aspx
Mime::Type.register "multipart/mixed", :mixed
class ActionController::Request
def initialize(env)
Thread.current[:request]=self
super
end
end
class MultiPartParamsParser
def main_part_name
"main"
end
def attachment_part_prefix
"my_company_attachment"
end
def content_type(main_part)
# TODO ----
:xml_simple
end
def content(main_part)
# TODO implement this
if main_part.is_a?(String)
main_part.gsub!("Content-Type: application/xml",'') # remove content type if it exists
main_part.strip! # to remove any trailing or leading whitespaces
else
main_part[:tempfile].read
end
end
def request
Thread.current[:request]
end
def parse_formatted_parameters(data)
multi_parts = Rack::Utils::Multipart.parse_multipart(request.try(:env))
main_part = multi_parts[main_part_name]
data = content(main_part)
# TODO return an error if data is not found
params = case content_type(main_part)
when :xml_simple, :xml_node
data.blank? ? {} : Hash.from_xml(data).with_indifferent_access
when :yaml
YAML.load(data)
when :json
if data.blank?
{}
else
ret = ActiveSupport::JSON.decode(data)
ret = {:_json => data} unless data.is_a?(Hash)
ret.with_indifferent_access
end
else
{}
end
process_attachments(params, multi_parts)
params
end
def process_attachments(data, multi_parts)
data.each do |key, value|
value ||= key # when array value is nil
if value.is_a?(Hash) or value.is_a?(Array)
process_attachments(value, multi_parts)
elsif value.respond_to?(:match) and value.match("^#{attachment_part_prefix}") and (attachment=multi_parts[value]) # there could Time,Numbers etc.., but we match only string.
data[key] = create_uploaded_file(attachment) # TODO handle the scenarios for short strings
end
end
end
def create_uploaded_file (attachment)
upload = attachment[:tempfile]
upload.extend(ActionController::UploadedFile)
upload.original_path = attachment[:filename]
upload.content_type = attachment[:type]
upload
end
end
proc = Proc.new do |data|
MultiPartParamsParser.new.parse_formatted_parameters(data)
end
ActionController::Base.param_parsers[Mime::Type.lookup('multipart/mixed')] = proc
then, you can post your message like this. Nesting gets automatically taken with no further changes to the models or controllers.
POST /posts.xml HTTP/1.1
Content-type: multipart/mixed; boundary=---------------------------###987612345###
Accept: application/xml,text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Cache-Control: no-cache
Pragma: no-cache
Connection: keep-alive
Content-Length: ##
-----------------------------###987612345###
content-disposition: name="main"
Content-Length: ##
<post><title>post_title</title><body>post_body</body>
<assets_attributes type="array">
<asset><data>my_company_attachment_0</data> </asset>
<asset><data>my_company_attachment_1</data> </asset>
</assets_attributes>
</post>
-----------------------------###987612345###
content-disposition: name="my_company_attachment_0"; filename="C:/Users/mv288/files/1.txt"
content-type: application/octet-stream
ÿþ
sample file content
-----------------------------###987612345###
content-disposition: name="my_company_attachment_1"; filename="C:/Users/mv288/Pictures/1.png"
content-type: image/png
ÿþ
sample file content
-----------------------------###987612345###