1

我正在将 pdf 文件从 iOS 应用程序上传到 Rails 应用程序。文件已上传,但其内容类型已损坏。

这是上传文件的 iOS 代码的相关部分(使用 AFNetworking 库):

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];

让我们转到另一边,Rails 3.2.3 服务器。
我正在使用 gem PaperClip(版本 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 客户端(例如 Transmit)直接从 S3 下载文件会得到相同的损坏文件。

问题是什么?您认为我应该在哪里进一步排除故障,客户端还是服务器?如何?有任何想法、提示、预感要断言或查看的事情吗?

如果您没有任何解决方案,我也很高兴在下载损坏文件后获得临时修复以再次正确设置损坏文件的 content_type(或任何问题)。

4

1 回答 1

3

如果您将上传的文件命名为“upload.pdf”而不是“upload.pfd”,会发生什么?

于 2012-08-02T22:44:16.507 回答