upyun / python-sdk

UPYUN Python SDK
117 stars 29 forks source link

UnsupportedOperation - fileno #11

Closed virusdefender closed 10 years ago

virusdefender commented 10 years ago

最近在使用django + 又拍云,但是直接网页上传文件的时候出现这个问题。先读取本地文件再上传就没问题。 代码大致是这样的,request.FILES["file"]是网页form上传来的。

up = Upload()
uploaded_file = request.FILES["file"]
#print os.fstat(uploaded_file.fileno()).st_size
file_path = up.upload_private_pic("/dealers/" + str(dealer.id) + "/images/", uploaded_file)
print file_path

Upload就是自己写的一个类,简单的集成了sdk中的上传文件的功能。但是sdk中有一句fileno的语句会导致错误,和我上面注释掉的那句基本一样。

截图在这里 http://photo.weibo.com/3560808645/albums/detail/album_id/3616760132684350#!/mode/1/page/1

timebug commented 10 years ago

Hi, @virusdefender

请测试下最新 master 的代码,https://github.com/upyun/python-sdk/commit/057266dbff6f1ce91389cd97139b5b78ea1c958b 这个 commit 已经修复该问题。

virusdefender commented 10 years ago

最新的commit测试可以使用。但是我修改的结果是

if hasattr(value, 'fileno'):
            length = value.size

就改了这一个地方。你们的测试用例在我的电脑上编码错误,环境ubuntu 14.04,就没有测试。但是可以使用。

timebug commented 10 years ago

@virusdefender 你提到的编码错误,有更具体的错误信息提供吗?

我们的测试用例在 Mac 和 ubuntu/14.04 上都测试过的,系统编码都是 en_US.UTF-8,均未出现错误。

PS. 既然最新的代码已经修复了这个问题,这个 issues 暂时就先关闭了,后续有问题请再反馈 :-)

timebug commented 10 years ago

Hi, @virusdefender

我又稍微完善了下对于这类 File Like Object 的支持 https://github.com/upyun/python-sdk/commit/358d48742ae5dc82a509535f7233806e10bddc9b ,兼容了自定义的 UploadObject。

有空请麻烦再测试下最新的修改,若没问题的话,稍后我就 Release 一个新的版本出来:v2.2.4。

另外,你前面提到:

length = value.size

类似这样的语句也能获取到 Django File Object 的大小是因为 Django 封装的文件对象提供了 size 属性,具体可参见 Django UploadObject 类的实现:

https://github.com/django/django/blob/master/django/core/files/uploadedfile.py#L30

而我们的修复方式更加通用点,在 Django InMemoryUploadedFile 的情况下,其虽然拥有 fileno 属性,但若尝试去获取大小的话就会抛 IOError 异常,因为此时传递给 InMemoryUploadedFile 的是一个 BytesIO 对象:

https://github.com/django/django/blob/master/django/core/files/utils.py#L12

https://github.com/django/django/blob/master/django/core/files/uploadhandler.py#L175

因此当 fileno 获取 size 失败的时候,我们就尝试用 getvalue() 方法去获取 BytesIO 对象的 size。这样就既能兼容 Django 中 InMemoryUploadedFileTemporaryUploadedFile 两种情况的 UploadObject ,也能兼容原始的 BytesIOStringIO 类构造的 File Like Object 了。

另外,特别地,Django File Object 实际上已经提供了一个 __len__ 的内置类属性:

https://github.com/django/django/blob/master/django/core/files/base.py#L36

因此,这里我们其实简单地将 hasattr(value, '__len__') 的判断提前也能解决这个问题 :-)

timebug commented 10 years ago

@virusdefender

v2.2.4 已经发布:# pip install -U upyun

virusdefender commented 10 years ago

十分感谢,今天白天有点事情。测试的问题,我刚才又跑了一遍,认真的看了一下,虽然最后面是经典的编码错误的提示,但是向前看是缺少了依赖的openssl的库的问题,有空再测试。再次表示感谢,国庆快乐~