2009-03-29 22 views
24

Tôi thừa kế một ứng dụng Rails 2.2.2 lưu trữ hình ảnh do người dùng tải lên trên Amazon S3. Mô hình Photo của attachment_fu dựa trên cung cấp phương thức rotate sử dụng open-uri để truy xuất hình ảnh từ S3 và MiniMagick để thực hiện xoay vòng.Tại sao Ruby open-uri lại trả về một StringIO trong bài kiểm thử đơn vị của tôi, nhưng một FileIO trong bộ điều khiển của tôi?

Phương pháp rotate chứa dòng này để lấy lại hình ảnh để sử dụng với MiniMagick:

temp_image = MiniMagick::Image.from_file(open(self.public_filename).path) 

self.public_filename lợi nhuận giống như

http://s3.amazonaws.com/bucketname/photos/98/photo.jpg 

Lấy hình ảnh và xoay nó chỉ làm việc tốt trong ứng dụng đang chạy trong sản xuất và phát triển. Tuy nhiên, các đơn vị kiểm tra không thành công với

TypeError: can't convert nil into String 
    /Users/santry/Development/totspot/vendor/gems/mini_magick-1.2.3/lib/mini_magick.rb:34:in `initialize' 
    /Users/santry/Development/totspot/vendor/gems/mini_magick-1.2.3/lib/mini_magick.rb:34:in `open' 
    /Users/santry/Development/totspot/vendor/gems/mini_magick-1.2.3/lib/mini_magick.rb:34:in `from_file' 

Lý do là khi các phương pháp mô hình được gọi là trong bối cảnh của bài kiểm tra đơn vị, open(self.public_filename) được trả lại một đối tượng StringIO có chứa các dữ liệu hình ảnh. Phương pháp path trên đối tượng này trả về nilMiniMagick::Image.from_file sẽ thổi lên.

Khi phương thức mô hình rất giống này được gọi từ PhotosController, open(self.public_filename) trả về một ví dụ FileIO được liên kết với một tệp có tên, ví dụ: /tmp/open-uri7378-0 và tệp chứa dữ liệu hình ảnh.

Suy nghĩ nguyên nhân phải có sự khác biệt về môi trường giữa thử nghiệm và phát triển, tôi đã kích hoạt bảng điều khiển trong môi trường phát triển. Nhưng cũng giống như trong thử nghiệm đơn vị, open('http://...') đã trả về một StringIO, không phải một FileIO.

Tôi đã truy tìm theo cách của mình thông qua open-uri và tất cả mã dành riêng cho ứng dụng có liên quan và không tìm thấy lý do nào cho sự khác biệt.

Trả lời

22

Mã chịu trách nhiệm về điều này là trong lớp Bộ đệm trong mở-uri. Nó bắt đầu bằng cách tạo ra một đối tượng StringIO và chỉ tạo ra một tệp tạm thời thực sự trong hệ thống tệp cục bộ khi dữ liệu vượt quá một kích thước nhất định (10 KB).

Tôi giả định rằng bất kỳ dữ liệu nào mà thử nghiệm của bạn đang tải đủ nhỏ để được giữ trong một StringIO và hình ảnh bạn đang sử dụng trong ứng dụng thực tế đủ lớn để đảm bảo một TempFile. Giải pháp là sử dụng phương pháp đó là chung cho cả hai lớp, đặc biệt là phương pháp đọc, với MiniMagick :: Hình ảnh # from_blob:

temp_image = MiniMagick::Image.from_blob(open(self.public_filename, &:read)) 
+3

Đừng làm 'mở (self.public_filename) .read', bạn không biết khi nào tay cầm sẽ bị đóng. Sử dụng 'open (self.public_filename, &: read)' thay vào đó, sử dụng block-form và đóng một cách rõ ràng khi hoàn thành. Và nó không thực sự mã hơn. – apeiros

+0

FYI, 'from_blob' hiện không còn được dùng để ưu tiên' đọc'. Xem https://github.com/probablycorey/mini_magick/blob/f309fbf390cd21a845264bca9bec95b9bdae8029/lib/mini_magick.rb#L82 –

61

Thư viện mở uri sử dụng một hằng số để thiết lập các giới hạn kích thước 10KB cho StringIO các đối tượng.

> OpenURI::Buffer::StringMax 
=> 10240 

Bạn có thể thay đổi cài đặt này thành 0 để ngăn chặn open-uri không bao giờ tạo đối tượng StringIO. Thay vào đó, điều này sẽ buộc nó luôn tạo ra một tệp tạm thời.

Chỉ cần ném này trong một initializer:

# Don't allow downloaded files to be created as StringIO. Force a tempfile to be created. 
require 'open-uri' 
OpenURI::Buffer.send :remove_const, 'StringMax' if OpenURI::Buffer.const_defined?('StringMax') 
OpenURI::Buffer.const_set 'StringMax', 0 

Bạn không thể chỉ cần đặt hằng số trực tiếp.Bạn cần phải thực sự loại bỏ hằng số và sau đó đặt nó một lần nữa (như trên), nếu không bạn sẽ nhận được một cảnh báo:

warning: already initialized constant StringMax 

CẬP NHẬT 2012/12/18: Rails 3 không yêu cầu OpenURI theo mặc định , vì vậy bạn cần thêm require 'open-uri' ở đầu trình khởi tạo. Tôi đã cập nhật mã ở trên để phản ánh thay đổi đó.

+0

Micah, Điều này có vẻ tuyệt vời nhưng tôi gặp lỗi khi tôi thử trong ứng dụng Rails 3. Dường như đối tượng Buffer chưa được tạo. Uninitialized hằng số OpenURI (NameError) – Paul

+8

Tôi nghĩ rằng tôi yêu bạn. –

+0

Cảm ơn, Paul, tôi đã cập nhật mã trong câu trả lời. Cảm ơn, Michael, anh cũng yêu em. –

Các vấn đề liên quan