6

Flask-uploadsを使用して、Flask サーバーにファイルをアップロードしています。許可される最大サイズは、 を使用して設定されflaskext.uploads.patch_request_class(app, 16 * 1024 * 1024)ます。

私のクライアント アプリケーション (単体テスト) は、要求を使用して、大きすぎるファイルを投稿します。

サーバーが status の HTTP 応答を返していることがわかります413: Request Entity Too Large。しかし、クライアントはリクエストコードで例外を発生させます

ConnectionError: HTTPConnectionPool(host='api.example.se', port=80): Max retries exceeded with url: /images (Caused by <class 'socket.error'>: [Errno 32] Broken pipe)

私の推測では、サーバーは受信ソケットを切断し、応答をクライアントに送り返します。しかし、クライアントが壊れた送信ソケットを取得すると、例外が発生し、応答がスキップされます。

質問:

  • Flask-Uploads とリクエストについての私の推測は正しいですか?
  • Flask-Uploads と request は 413 エラーを正しく処理しますか?
  • 投稿が大きすぎる場合、クライアント コードが何らかの html を返すことを期待する必要がありますか?

アップデート

これは私の問題を再現する簡単な例です。

サーバー.py

from flask import Flask, request
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 1024

@app.route('/post', methods=('POST',))
def view_post():
    return request.data

app.run(debug=True)

client.py

from tempfile import NamedTemporaryFile
import requests

def post(size):
    print "Post with size %s" % size,
    f = NamedTemporaryFile(delete=False, suffix=".jpg")
    for i in range(0, size):
        f.write("CoDe")
    f.close()

    # Post
    files = {'file': ("tempfile.jpg", open(f.name, 'rb'))}
    r = requests.post("http://127.0.0.1:5000/post", files=files)
    print "gives status code = %s" % r.status_code

post(16)
post(40845)
post(40846)

クライアントからの結果

Post with size 16 gives status code = 200
Post with size 40845 gives status code = 413
Post with size 40846
Traceback (most recent call last):
  File "client.py", line 18, in <module>
    post(40846)
  File "client.py", line 13, in post
    r = requests.post("http://127.0.0.1:5000/post", files=files)
  File "/opt/python_env/renter/lib/python2.7/site-packages/requests/api.py", line 88, in post
    return request('post', url, data=data, **kwargs)
  File "/opt/python_env/renter/lib/python2.7/site-packages/requests/api.py", line 44, in request
    return session.request(method=method, url=url, **kwargs)
  File "/opt/python_env/renter/lib/python2.7/site-packages/requests/sessions.py", line 357, in request
    resp = self.send(prep, **send_kwargs)
  File "/opt/python_env/renter/lib/python2.7/site-packages/requests/sessions.py", line 460, in send
    r = adapter.send(request, **kwargs)
  File "/opt/python_env/renter/lib/python2.7/site-packages/requests/adapters.py", line 354, in send
    raise ConnectionError(e)
requests.exceptions.ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=5000): Max retries exceeded with url: /post (Caused by <class 'socket.error'>: [Errno 32] Broken pipe)

私のバージョン

$ pip freeze
Flask==0.10.1
Flask-Mail==0.9.0
Flask-SQLAlchemy==1.0
Flask-Uploads==0.1.3
Jinja2==2.7.1
MarkupSafe==0.18
MySQL-python==1.2.4
Pillow==2.1.0
SQLAlchemy==0.8.2
Werkzeug==0.9.4
blinker==1.3
itsdangerous==0.23
passlib==1.6.1
python-dateutil==2.1
requests==2.0.0
simplejson==3.3.0
six==1.4.1
virtualenv==1.10.1
voluptuous==0.8.1
wsgiref==0.1.2
4

3 に答える 3

7

Flask は接続を閉じています。413 エラーのエラー ハンドラーを設定できます。

@app.errorhandler(413)
def request_entity_too_large(error):
    return 'File Too Large', 413

これで、クライアントは 413 エラーを受け取るはずです。このコードはテストしていないことに注意してください。

アップデート:

413 エラーを再現しようとしましたが、ConnectionError 例外は発生しませんでした。

簡単な例を次に示します。

from flask import Flask, request

app = Flask(__name__)

app.config['MAX_CONTENT_LENGTH'] = 1024


@app.route('/post', methods=('POST',))
def view_post():
    return request.data


app.run(debug=True)

ファイルを実行した後、ターミナルを使用してリクエストをテストし、大きなデータを送信しました。

>>> import requests
>>> r = requests.post('http://127.0.0.1:5000/post', data={'foo': 'a'})
>>> r
<Response [200]>
>>> r = requests.post('http://127.0.0.1:5000/post', data={'foo': 'a'*10000})
>>> r
<Response [413]>
>>> r.status_code
413
>>> r.content
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n<title>413 Request Entity Too Large</title
>\n<h1>Request Entity Too Large</h1>\n<p>The data value transmitted exceeds the capacity limit.</p>\n'

ご覧のとおり、flask 413 エラーから応答があり、リクエストは例外を発生させませんでした。

ちなみに私が使っているのは:

  • フラスコ: 0.10.1
  • リクエスト: 2.0.0
于 2013-10-19T15:29:03.543 に答える
4

HTTP 1.1 の仕様であるRFC 2616には、次のように記載されています。

10.4.14 413 要求エンティティが大きすぎます

要求
エンティティが、サーバーが処理しようとしている、または処理できるサイズよりも大きいため、サーバーは要求の処理を拒否しています。サーバーは
、クライアントがリクエストを続行するのを防ぐために接続を閉じることができます。


状態が一時的なものである場合、サーバーは Retry- After ヘッダー フィールドを含めて、それが一時的であることを示し
、クライアントが再試行できる時間後に含める必要があります (SHOULD) 。

これがここで起こっていることです。フラスコは接続を閉じて、クライアントがアップロードを続行できないようにしているため、Broken pipeエラーが発生しています。

于 2013-10-18T21:24:54.570 に答える
0

この github issue の回答に基づいています ( https://github.com/benoitc/gunicorn/issues/1733#issuecomment-377000612 )

@app.before_request
def handle_chunking():
    """
    Sets the "wsgi.input_terminated" environment flag, thus enabling
    Werkzeug to pass chunked requests as streams.  The gunicorn server
    should set this, but it's not yet been implemented.
    """

    transfer_encoding = request.headers.get("Transfer-Encoding", None)
    if transfer_encoding == u"chunked":
        request.environ["wsgi.input_terminated"] = True
于 2019-12-16T07:28:06.627 に答える