【问题标题】:Reading a docx file from s3 bucket with flask results in an AttributeError使用烧瓶从 s3 存储桶中读取 docx 文件会导致 AttributeError
【发布时间】:2021-05-20 02:59:04
【问题描述】:

我遇到了很多不同的错误,我什至不知道应该提及哪个,但这与凭据无关,因为我已经可以上传文件并且可以读取 txt 文件。现在我想读一个docx。

我在我的 index.html 中创建了一个表单,其中只有一个文本区域来写入文件的确切名称和一个提交输入,该输入将打开一个新窗口,其中包含我的 AWS S3 存储桶中我的 docx 文件的段落数。

我得到的错误是:

AttributeError:“StreamingBody”对象没有“seek”属性

我的代码如下所示:

path = "s3://***bucket/"
bucket_name = "***bucket"

@app.route('/resultfiles', methods=["POST"])
def getdata():
    thefilename = request.form['file_name']

    if '.docx' in thefilename:
        
        object_key = thefilename
        file_object = client.get_object(Bucket=bucket_name, Key=object_key)
        body = file_object['Body']
        
        doc = docx.Document(body)
        docx_paras = len(doc.paragraphs)
    
    return render_template('resultfiles.html', docx_paras=docx_paras)

【问题讨论】:

  • 你可能只需要在你的 body 上调用 read() 方法:body = file_object['Body'].read() 然后你会得到一个二进制对象,它可能会或可能不会被 docx 库使用。
  • 我尝试了 read() 但它不起作用,我也尝试了 read().decode('utf-8') 但仍然没有。你没有别的了吗? .

标签: python amazon-web-services flask amazon-s3 python-docx


【解决方案1】:

我查看了 python-docx 的文档,特别是 Document-constructor:

docx.Document(docx=None)

返回从 docx 加载的 Document 对象,其中 docx 可以是 .docx 文件(字符串)的路径或类似文件的对象。如果 docx 缺失或没有,则加载内置的默认文档“模板”。

似乎需要一个类似文件的对象或文件的路径。我们可以将我们从 boto3 得到的不同表示转换成一个类似文件的对象,这里有一些示例代码:

import io

import boto3
import docx

BUCKET_NAME = "my-bucket"

def main():
    s3 = boto3.resource("s3")
    bucket = s3.Bucket(BUCKET_NAME)

    object_in_s3 = bucket.Object("test.docx")
    object_as_streaming_body = object_in_s3.get()["Body"]
    print(f"Type of object_as_streaming_body: {type(object_as_streaming_body)}")
    object_as_bytes = object_as_streaming_body.read()
    print(f"Type of object_as_bytes: {type(object_as_bytes)}")

    # Now we use BytesIO to create a file-like object from our byte-stream
    object_as_file_like = io.BytesIO(object_as_bytes)
    
    # Et voila!
    document = docx.Document(docx=object_as_file_like)

    print(document.paragraphs)

if __name__ == "__main__":
    main()

这就是它的样子:

$ python test.py
Type of object_as_streaming_body: <class 'botocore.response.StreamingBody'>
Type of object_as_bytes: <class 'bytes'>
[<docx.text.paragraph.Paragraph object at 0x00000258B7C34A30>]

【讨论】:

  • 成功了!非常感谢!
  • @Maurice 我们可以分块读取这个文件数据吗?(如果文件是 1 MB 并且我只想读取前 50KB 的数据)。
  • 原则上是的,但是你会得到不可用的数据,因为这不是纯文本,而是一个复杂的压缩对象。
  • 您能告诉我如何实现该功能吗?因为如果文件大小为 1GB,那么它只会增加 CPU 内存使用量。
  • 也许我应该更清楚一点:当您使用 word 文档时,这是不可能的。如果您想下载 S3 对象的块,您可以研究如何使用 range 标头。
猜你喜欢
  • 1970-01-01
  • 2021-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-04
  • 2019-09-19
  • 2016-07-12
  • 1970-01-01
相关资源
最近更新 更多