使用 Turbogears 2 上传文件

发布于 2024-08-23 21:37:01 字数 755 浏览 4 评论 0原文

我一直在尝试找出使用 Turbogears 2 管理文件上传的“最佳实践”方法,但到目前为止还没有真正找到任何示例。我已经找到了一种实际上传文件的方法,但我不确定它的可靠性如何。

另外,获取上传文件名的好方法是什么?

    file = request.POST['file']
    permanent_file = open(os.path.join(asset_dirname,
        file.filename.lstrip(os.sep)), 'w')
    shutil.copyfileobj(file.file, permanent_file)
    file.file.close()
    this_file = self.request.params["file"].filename 
    permanent_file.close()

因此,假设我理解正确,这样的事情会避免核心“命名”问题吗? id = UUID。

    file = request.POST['file']
    permanent_file = open(os.path.join(asset_dirname,
        id.lstrip(os.sep)), 'w')
    shutil.copyfileobj(file.file, permanent_file)
    file.file.close()
    this_file = file.filename
    permanent_file.close()

I've been trying to work out the 'best practices' way to manage file uploads with Turbogears 2 and have thus far not really found any examples. I've figured out a way to actually upload the file, but I'm not sure how reliable it us.

Also, what would be a good way to get the uploaded files name?

    file = request.POST['file']
    permanent_file = open(os.path.join(asset_dirname,
        file.filename.lstrip(os.sep)), 'w')
    shutil.copyfileobj(file.file, permanent_file)
    file.file.close()
    this_file = self.request.params["file"].filename 
    permanent_file.close()

So assuming I'm understanding correctly, would something like this avoid the core 'naming' problem? id = UUID.

    file = request.POST['file']
    permanent_file = open(os.path.join(asset_dirname,
        id.lstrip(os.sep)), 'w')
    shutil.copyfileobj(file.file, permanent_file)
    file.file.close()
    this_file = file.filename
    permanent_file.close()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

许久 2024-08-30 21:37:01

我只是希望任何来这里寻找答案的人都知道 Allesandro Molina 的伟大图书馆Depot 构成了这个问题的最佳答案。

它解决了命名和复制问题,并且可以很好地融入您的 TurboGears 应用程序中。您可以将其与 MongoDB GridFS 一起使用,如下例所示:

from depot.manager import DepotManager

# Configure a *default* depot to store files on MongoDB GridFS
DepotManager.configure('default', {
    'depot.backend': 'depot.io.gridfs.GridFSStorage',
    'depot.mongouri': 'mongodb://localhost/db'
})

depot = DepotManager.get()

# Save the file and get the fileid
fileid = depot.create(open('/tmp/file.png'))

# Get the file back
stored_file = depot.get(fileid)
print stored_file.filename
print stored_file.content_type

或者您可以轻松地在 SQLAlchemy 模型中创建附件字段,例如:

from depot.fields.sqlalchemy import UploadedFileField

class Document(Base):
    __tablename__ = 'document'

    uid = Column(Integer, autoincrement=True, primary_key=True)
    name = Column(Unicode(16), unique=True)

    content = Column(UploadedFileField)

…然后,存储带有附加文件的文档(源可以是文件或字节)变得像这样简单:

doc = Document(name=u'Foo', content=open('/tmp/document.xls'))
DBSession.add(doc)

Depot 同时支持 LocalFileStorageMongoDBGridFSStorage,以及亚马逊的 S3Storage。并且,至少对于存储在本地和 S3 中的文件,fileid 将由 uuid.uuid1() 生成。

I just want anyone who comes here looking for answers, to know that Allesandro Molina's great library Depot constitutes the best answer to this question.

It solves both the naming and copying issues, and it will incorporate nicely into your TurboGears application. You can use it with MongoDB GridFS, as in this example:

from depot.manager import DepotManager

# Configure a *default* depot to store files on MongoDB GridFS
DepotManager.configure('default', {
    'depot.backend': 'depot.io.gridfs.GridFSStorage',
    'depot.mongouri': 'mongodb://localhost/db'
})

depot = DepotManager.get()

# Save the file and get the fileid
fileid = depot.create(open('/tmp/file.png'))

# Get the file back
stored_file = depot.get(fileid)
print stored_file.filename
print stored_file.content_type

or you can easily create attachment fields in your SQLAlchemy models, like:

from depot.fields.sqlalchemy import UploadedFileField

class Document(Base):
    __tablename__ = 'document'

    uid = Column(Integer, autoincrement=True, primary_key=True)
    name = Column(Unicode(16), unique=True)

    content = Column(UploadedFileField)

… and then, storing documents with attached files (the source can be a file or bytes) becomes as easy as:

doc = Document(name=u'Foo', content=open('/tmp/document.xls'))
DBSession.add(doc)

Depot supports both LocalFileStorage, MongoDB's GridFSStorage, and Amazon's S3Storage. And, at least for files stored locally and in S3, the fileid will be generated by uuid.uuid1().

厌味 2024-08-30 21:37:01

@mhawke - 你是对的,你必须处理这个问题 - 取决于你对文件所做的事情,如果名称冲突并不重要,例如你只关心某些数据的最新版本,那么可能没有问题,或者如果文件名实际上并不重要,只是文件内容,但它仍然是不好的做法。

您可以在 tmp 目录中使用命名的临时文件,然后在验证后将该文件移动到其最终位置。或者您可以检查文件名是否已经存在,如下所示:

file.name = slugify(myfile.filename)
name, ext = os.path.splitext(file.name)
while os.path.exists(os.path.join(permanent_store, file.name)):
    name += '_'
    file.name = name + ext

raw_file = os.path.join(permanent_store, file.name)

slugify 方法将用于整理文件名...

@mhawke - you're right you have to handle that - depends on what you are doing with the file, if it doesn't matter if there is a name collision eg you only care for the latest version of some data then theres probably no issue, or if the filename isn't actually important just the file contents, but its still bad practice.

You could use a named tempfile in a tmp dir, then move the file once validated to its final location. Or you could check the filename doesn't already exist like so:

file.name = slugify(myfile.filename)
name, ext = os.path.splitext(file.name)
while os.path.exists(os.path.join(permanent_store, file.name)):
    name += '_'
    file.name = name + ext

raw_file = os.path.join(permanent_store, file.name)

The slugify method would be used to tidy up the filename...

踏月而来 2024-08-30 21:37:01

我不太了解 Turbogears 以及它是否可以提供任何东西来避免以下情况,但在我看来,这段代码充满了危险。恶意用户可能会覆盖(或创建)Turbogears python 进程具有写访问权限的任何文件。

如果 asset_dirname/tmpfile.filename 的内容是 ../../../../ ../../../etc/passwd 和文件 root::0:0:root:/root:/bin/bash 的内容?在 UNIX 环境中,此代码(权限待定)将以截断方式打开文件 /tmp/../../../../../../../etc/passwd模式,然后将上传文件的内容复制到其中 - 有效地覆盖系统的密码文件并指定没有密码的 root 用户。想必 Windows 机器也可以做一些令人讨厌的事情。

好的,这是一个极端的例子,要求 python 以 root 身份运行(没有人这样做,不是吗?)。即使Python以低权限用户运行,之前上传的文件也可能被随意覆盖。

总而言之,不要信任用户输入,在本例中,用户提供的文件名在 file.filename 中可用。

I don't know much about Turbogears and whether it can provide anything to avoid the following, but it seems to me that this code is fraught with danger. It may be possible for a malicious user to overwrite (or create) any file that the Turbogears python process has write access to.

What if asset_dirname is /tmp, the contents of file.filename is ../../../../../../../etc/passwd and the contents of the file root::0:0:root:/root:/bin/bash? In a UNIX environment this code (permissions pending) would open the file /tmp/../../../../../../../etc/passwd in truncate mode and then copy the contents of the uploaded file to it - effectively overwriting your system's password file and specifying a root user without a password. Presumably there are nasty things that can be done to a Windows machine too.

OK, this is an extreme example that requires that python is running as root (no one does that, do they?). Even if python is running as a low-priveleged user, previously uploaded files could be overwritten at will.

To summarise, don't trust user input, in this case the user supplied filename that is available in file.filename.

草莓酥 2024-08-30 21:37:01

涡轮齿轮不就是带有附加装置的塔架吗?您可以查看那里的帮助:

http://wiki.pylonshq。 com/display/pylonsdocs/Form+Handling#file-uploads

但是,这仍然包含 mhawke 提到的潜在安全缺陷:

os.path.join(permanent_store, myfile.filename.lstrip(os.sep))

如果文件名某种程度上是 ../../../ ,则与上面相同../../etc/passwd 然后你可以替换该文件...

所以你可以像这样获取实际的文件名:

os.path.join(permanent_store, myfile.filename.split(os.sep).pop())

Isn't turbogears just pylons with extras? You could check out the help there:

http://wiki.pylonshq.com/display/pylonsdocs/Form+Handling#file-uploads

However, that still contains the potential security flaw that mhawke mentioned:

os.path.join(permanent_store, myfile.filename.lstrip(os.sep))

Same as above really if filename somehow was ../../../../../etc/passwd then you could replace that file...

So you could just get the actual filename like so:

os.path.join(permanent_store, myfile.filename.split(os.sep).pop())
む无字情书 2024-08-30 21:37:01

Werkzeug 有一个非常好的帮助函数来保护文件名,名为 secure_filename< /a>.我认为您可以采用并使用它。

Werkzeug has a very good helper function for securing file names called secure_filename. I think you can adopt and use it.

仅此而已 2024-08-30 21:37:01

关于如何走,我会赞同已经给出的好答案。

这是我关于存储文件命名的 2 便士。

事实上,使用原始名称保存文件可能会导致漏洞。
我对原始名称的唯一用途(如果有的话)是暗示 MIME 类型检测。

无论如何,要保存的文件应该通过记录标识或类似的东西指定唯一的名称,并保存在应用程序目录所有者(普通用户)控制下的位置,或保存在某些其他存储服务中,如上所述depot 等。

这是跨语言良好系统设计的问题:)。

about how to go, i'd second the good answers already given.

here's my 2 pence about the stored file naming.

indeed saving the file using the original name may cause a vulnerability.
the only use i make of the original name, if at all, is to hint the mime type detection.

anyway the files to be saved should be given unique names, by a record identity or something similar, and kept in a place under the control of the app directory owner, who is a regular user, or in some other storage service, as the aforementioned depot etc.

it's a matter of cross language good system design :).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文