Vì vậy, đây là vấn đề, bạn có một luồng [đối tượng giống như tệp] trong Python và bạn muốn chuyển nội dung của luồng đó vào kho lưu trữ zip. Âm thanh như một yêu cầu phổ biến? . Tôi đề xuất một giải pháp ở đây với móc
Có hai phương pháp để ghi dữ liệu vào tệp zip trong mô-đun zipfile của Python
ZipFile.write[filename[, arcname[, compress_type]]]
và
ZipFile.writestr[zinfo_or_arcname, bytes[, compress_type]]
Cái đầu tiên lấy tên của một tệp, mở nó và chuyển nội dung vào kho lưu trữ theo khối 8K. Nghe có vẻ phù hợp với những gì tôi muốn ngoại trừ việc tôi có một đối tượng giống như tệp, không phải tên tệp và ZipFile. viết sẽ không chấp nhận điều đó. Tôi có thể tạo một tệp tạm thời trên đĩa và ghi dữ liệu của mình vào đó, sau đó chuyển tên của tệp thay vào đó nhưng điều đó giả sử [a] rằng tôi có quyền truy cập vào hệ thống tệp để ghi và [b] tôi không ngại lưu trữ tệp
Trước khi bạn phản đối, đối tượng ZipFile chỉ yêu cầu một đối tượng giống như tệp có hỗ trợ tìm kiếm và cho biết, nó không thực sự phải là một tệp trong hệ thống tệp nên [a] vẫn là một kịch bản hợp lệ. Mặc dù vậy, chúng tôi sẽ phải loại bỏ mọi ý tưởng thông minh về việc lưu trực tiếp tệp zip qua kết nối mạng. Xem xét kỹ hơn quá trình triển khai cho chúng ta thấy rằng một khi dữ liệu đã được nén và ghi vào kho lưu trữ, luồng sẽ được quay trở lại tiêu đề của mục lưu trữ để cập nhật thông tin về kích thước nén và không nén. Tuy nhiên, ngay cả khi bạn đang đệm đầu ra thì ít nhất bạn đang xử lý dữ liệu nén nhỏ hơn chứ không phải nguồn không nén ban đầu
Vì vậy, nếu ZipFile. ghi không hoạt động đối với các luồng, còn việc sử dụng ZipFile thì sao. writetr thay thế? . Đối với các tệp lớn hơn, điều này khó có thể thực hiện được. Tôi đã thắc mắc về việc đánh lừa phương thức này bằng một đối tượng giống như chuỗi nhưng ngay cả khi tôi có thể làm điều này thì phương thức vẫn sẽ cố gắng tạo một chuỗi thông thường với toàn bộ dữ liệu được nén, điều này sẽ không hoạt động đối với các luồng lớn
Giải pháp 1
Giải pháp đầu tiên được lấy từ một gợi ý trên StackOverflow. Ý tưởng là bọc đối tượng ZipFile và viết một phương thức mới. Rõ ràng đó sẽ là một điều tốt để những người bảo trì mô-đun xem xét nhưng nó đòi hỏi phải sao chép mã đáng kể. Nếu tôi quá phụ thuộc vào nội bộ của việc triển khai đối tượng ZipFile, tôi cũng có thể xem liệu có cách nào tốt hơn không
Giải pháp 2
Nhìn vào việc triển khai ZipFile, phương thức ghi rõ ràng rất gần với những gì tôi muốn làm. Giá như nó chấp nhận một đối tượng giống như tệp. Nhìn kỹ hơn sẽ thấy rằng nó chỉ thực hiện hai việc với tên tệp đã truyền. Nó gọi hệ điều hành. stat và sau đó, ngay sau đó, gọi open để lấy một đối tượng giống như tệp
Điều này khiến tôi suy nghĩ liệu tôi có thể đánh lừa phương thức ghi để chấp nhận một thứ khác ngoài tên của tệp hay không. Tôi đã tạo một đối tượng [mà tôi gọi là VirtualFilePath] và đặt cho nó một phương thức stat và open. Việc triển khai không quan trọng, nhưng đối tượng này về cơ bản bao bọc đối tượng giống như tệp của tôi mô phỏng hai chức năng hệ điều hành này
Thật không may, tôi không thể chuyển VirtualFilePath tới chức năng mở hệ điều hành. Tôi sẽ gặp lỗi không mong đợi một phiên bản. Điều tương tự cũng xảy ra với hệ điều hành. thống kê. Tuy nhiên, tôi có thể viết các hook để chặn các cuộc gọi này và chuyển hướng các cuộc gọi đến các phương thức của mình nếu đối số là VirtualFilePath. Về cơ bản, giải pháp của tôi trông như thế nào
import os,__builtin__ stat_pass=os.stat open_pass=__builtin__.open def stat_hook[path]: if isinstance[path,VirtualFilePath]: return path.stat[] else: return stat_pass[path] def open_hook[path,*params]: if isinstance[path,VirtualFilePath]: return path.open[*params] else: return open_pass[path,*params] class ZipHooks[object]: hookCount=0 def __init__[self]: if not ZipHooks.hookCount: os.stat=stat_hook __builtin__.open=open_hook ZipHooks.hookCount+=1 def __enter__[self]: return self def __exit__[self, type, value, traceback]: self.Unhook[] def Unhook[self]: ZipHooks.hookCount-=1 if not ZipHooks.hookCount: os.stat=stat_pass __builtin__.open=open_pass
Mã này thêm các móc phát hiện đối tượng VirtualFilePath của tôi khi nó được chuyển sang open hoặc stat và chuyển hướng các cuộc gọi đó. Để quản lý các hook dễ dàng hơn, chúng ta tạo một đối tượng ZipHooks với các phương thức __enter__ và __exit__ cho phép nó được sử dụng trong câu lệnh 'with' như thế này
with ZipHooks[] as zh: # add stuff to an archive using VirtualFilePath here
Còn một chi tiết cuối cùng cần làm sáng tỏ. stat được cho là trả về kích thước của tệp nhưng nếu tôi không biết nó vì tôi đang đọc dữ liệu từ một luồng thì sao? . việc triển khai phương thức ghi cho thấy rằng nó không thực sự phụ thuộc vào kích thước được trả về bởi stat vì nó giám sát cả kích thước nén và không nén và nhồi lại tiêu đề khi nó theo dõi lại
Các bit thống kê duy nhất khác mà ZipFile. write quan tâm là ngày sửa đổi của tệp và chế độ [mà nó sử dụng để xác định xem tệp có thực sự là một thư mục hay không]. Vì vậy, nếu đối tượng giống như tệp của bạn hoàn toàn không giống tệp thì điều đó sẽ không quá quan trọng vì bạn chỉ phải giả mạo các trường này trong kết quả thống kê