iOSアプリケーションからRailsアプリケーションにPDFファイルをアップロードしています。ファイルはアップロードされますが、そのコンテンツタイプが破損しています。
(AFNetworkingライブラリを使用して)ファイルをアップロードするためのiOSコードの関連部分は次のとおりです。
NSURL *url = [NSURL URLWithString:kWebserviceHost];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
// httpClient.parameterEncoding = AFFormURLParameterEncoding; // Experimented with different values, no effect
NSData *documentData = [NSData dataWithContentsOfURL:self.documentURL];
if (! documentData) {
NSLog(@"Trying to upload nil document: sorry! - %@", self.documentData);
return;
}
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:@"PUT" // @"POST"
path:@"new_upload"
parameters:@{ @"fooKey" : fooString, @"barKey" : barString }
constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData:documentData name:@"document" fileName:@"upload.pfd" mimeType:@"application/pdf"];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setUploadProgressBlock:^(NSInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
NSLog(@"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
progressBlock(totalBytesWritten / totalBytesExpectedToWrite);
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
uploadSuccessBlock();
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Sorry, could not upload document - %@", error);
failureBlock(error);
}];
[operation start];
反対側のRails3.2.3サーバーに行きましょう。
宝石のペーパークリップ(バージョン3.1.4)を使用しています。モデルクラスのペーパークリップアタッチメントの定義は次のとおりです。
paperclip_storage = (Rails.env.production?) ? {
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:path => ':attachment/:id/:style.:extension',
:bucket => 'mybucketname' #,
# :s3_headers => { "Content-Type" => "video/mp4" }
}
: { :headers => { "Content-Type" => "application/pdf" }
}
has_attached_file :document, paperclip_storage.merge( :use_timestamp => false,
:url => "/assets/:id/:basename.:extension",
:path => ":rails_root/public/assets/:id/:basename.:extension",
:processors => nil # Maybe the processing of the file mess things up?
)
また、後処理をスキップしようとしました(スタイルが定義されていないため、ドキュメントには何も適用しないように記載されていますが)。
before_post_process :skip_post_process
def skip_post_process
return false
end
別の試み:post_processフィルターでcontent_typeを設定します:
after_post_process :force_content_type_to_pdf
def force_content_type_to_pdf
puts "--------------- Changing content_type"
self.document.instance_write(:content_type, "application/pdf")
end
ファイルを受信して保存するコントローラーの方法は次のとおりです。
def new_upload
@document = Document.create()
document = params[:document]
if (document.nil?)
return
end
@document.document = document
puts "Document type: #{@document.document.content_type}" # logs: 'application/pdf'
puts "Parameters: #{params}" # logs Parameters: {"fooKey"=>"foo", "barKey"=>"bar", "document"=>#<ActionDispatch::Http::UploadedFile:0x007ff5a429be58 @original_filename="upload.pfd", @content_type="application/pdf", @headers="Content-Disposition: form-data; name=\"document\"; filename=\"upload.pfd\"\r\nContent-Type: application/pdf\r\n", @tempfile=#<File:/var/folders/vy/zm_x1bts5hs6pkvzk7clnmvm0000gn/T/RackMultipart20120802-12714-1j0hq6q>>}
@document.foo = params['fooKey']
@document.bar = params['barKey']
if @document.save
render :json => {status: "saved" }
return
else
render json: @document.errors, status: :unprocessable_entity
return
end
end
ファイルはアップロードされていますが、読み取れません。Finderは、それを開くために使用するアプリケーションを認識していません。
QuickLookプレビューのスクリーンショットは次のとおりです。
コンテンツタイプがどういうわけか混同されています。
file --mime /file/path/before/or/after/upload.pdf
元のファイル(iOSアプリによってアップロードされるファイル)またはアップロードが成功した後にRailsサーバーによって作成されたファイルに次のメッセージを返します
/upload.pfd: application/pdf; charset=binary
。これまでのところ良さそうです。
mdls -name kMDItemContentType /file/path/before/upload.pdf
kMDItemContentType = "com.adobe.pdf"
アップロードするファイルに戻ります。それでも大丈夫です。ただし、Railsサーバーによって作成されたファイルに対する同じコマンドは次を返しますkMDItemContentType = "dyn.ah62d4rv4ge81a3xe"
。
これは、少なくともFinderが混乱している理由を説明しています。
ブラウザでファイルをダウンロードする場合の問題も同様です。これが私のコントローラーの関連する方法です:
def download_document
@document = Document.find(params[:id])
if (@document && @document.document)
# Line below propagate the file content-type problem:
send_file Rails.root.join(document.path), :type => "application/pdf", :x_sendfile => false, :stream => false
# This hack works while server is locally hosted
send_data File.read(Rails.root.join(@document.document.path)), :type => "application/pdf"
end
end
残念ながら、S3でホスティングする場合、ハックは問題ありません。また、iOSアプリをデバッグするときに、ファイルシステムを簡単に参照して、アップロードされたドキュメントを確認することができなくなります。
送信などのFTPクライアントを介してS3から直接ファイルをダウンロードすると、同じ破損したファイルが取得されます。
何が問題ですか?クライアントまたはサーバーのどこでさらにトラブルシューティングを行う必要があると思いますか?どのように?主張したり見たりするアイデア、ヒント、物事の束はありますか?
解決策がない場合は、ダウンロード後に破損したファイルにcontent_type(または問題が何であれ)を適切に再設定するための一時的な修正も提供されます。