我在使用cookiecutter-flask应用程序(v.10.0.1)中上传文件时遇到问题。现在,它没有保存上传的文件。
Cookiecutter-Flask默认安装WTForms和Flask-WTForms。我尝试过添加Flask-Uploads,但我不相信模块在这一点上添加了任何东西,所以我已经卸载了它。这是Flask-WTF文件上传文档:http://flask-wtf.readthedocs.io/en/latest/form.html#module-flask_wtf.file
文档和我的应用程序之间的主要区别在于我似乎有更多文件的信息,符合cookiecutter的约定。
在app_name/spreadsheet/forms.py
:
from flask_wtf import Form
from wtforms.validators import DataRequired
from flask_wtf.file import FileField, FileAllowed, FileRequired
class UploadForm(Form):
"""Upload form."""
csv = FileField('Your CSV', validators=[FileRequired(),FileAllowed(['csv', 'CSVs only!'])])
def __init__(self, *args, **kwargs):
"""Create instance."""
super(UploadForm, self).__init__(*args, **kwargs)
self.user = None
def validate(self):
"""Validate the form."""
initial_validation = super(UploadForm, self).validate()
if not initial_validation:
return False
在app_name/spreadsheet/views.py
:
from flask import Blueprint, render_template
from flask_login import login_required
from werkzeug.utils import secure_filename
from app_name.spreadsheet.forms import UploadForm
from app_name.spreadsheet.models import Spreadsheet
from app_name.utils import flash, flash_errors
blueprint = Blueprint('spreadsheet', __name__, url_prefix='/spreadsheets', static_folder='../static')
@blueprint.route('/upload', methods=['GET', 'POST']) #TODO test without GET since it won't work anyway
@login_required
def upload():
uploadform = UploadForm()
if uploadform.validate_on_submit():
filename = secure_filename(form.csv.data.filename)
uploadform.csv.data.save('uploads/csvs/' + filename)
flash("CSV saved.")
return redirect(url_for('list'))
else:
filename = None
return render_template('spreadsheets/upload.html', uploadform=uploadform)
这是我上传文件时没有错误的命令行输出:
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [04/Sep/2016 10:29:10] "GET /spreadsheets/upload HTTP/1.1" 200 -
127.0.0.1 - - [04/Sep/2016 10:29:10] "GET /_debug_toolbar/static/css/toolbar.css?0.3058158586562558 HTTP/1.1" 200 -
127.0.0.1 - - [04/Sep/2016 10:29:14] "POST /spreadsheets/upload HTTP/1.1" 200 -
127.0.0.1 - - [04/Sep/2016 10:29:14] "GET /_debug_toolbar/static/css/toolbar.css?0.3790246965220061 HTTP/1.1" 200 -
对于uploads/csvs
目录,我尝试过绝对路径和相对路径,并且目录已获得许可766。
模板文件是:
{% extends "layout.html" %}
{% block content %}
<h1>Welcome {{ session.username }}</h1>
{% with uploadform=uploadform %}
{% if current_user and current_user.is_authenticated and uploadform %}
<form id="uploadForm" method="POST" class="" action="{{ url_for('spreadsheet.upload') }}" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-group">
{{ uploadform.csv(class_="form-control") }}
</div>
<button type="submit" class="btn btn-default">Upload</button>
</form>
{% endif %}
{% endwith %}
{% endblock %}
哪个生成此HTML:
<form id="uploadForm" method="POST" class="" action="/spreadsheets/upload" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="LONG_RANDOM_VALUE"/>
<div class="form-group">
<input class="form-control" id="csv" name="csv" type="file">
</div>
<button type="submit" class="btn btn-default">Upload</button>
</form>
答案 0 :(得分:1)
查看文档后,您提供的链接表明data
的{{1}}字段是csv
的实例。 FileStorage.save()
的文档表明:
如果目的地是文件对象,则必须在通话后自行关闭目的地。
可能是因为你没有关闭文件,它没有被写入磁盘吗?
答案 1 :(得分:1)
试试这个:
from flask import request
if uploadform.validate_on_submit():
if 'csv' in request.files:
csv = request.files['csv']
csv.save('uploads/csvs/' + csv.filename)
答案 2 :(得分:1)
问题的主要原因在于:
def validate(self):
"""Validate the form."""
initial_validation = super(UploadForm, self).validate()
if not initial_validation:
return False
所以validate
类的UploadForm
方法。
让我们快速调查一下这里发生了什么。
在views.py
行中:
if uploadform.validate_on_submit():
flask_wtf
包调用validate
方法。所以再看看你的覆盖方法:
def validate(self):
"""Validate the form."""
initial_validation = super(UploadForm, self).validate()
if not initial_validation:
return False
这里有什么问题?如果initial_validation
为True
,则您的validate
方法将返回None
。那应该怎么办?只有html渲染:
def upload():
uploadform = UploadForm()
if uploadform.validate_on_submit(): # <--- here it's None
filename = secure_filename(form.csv.data.filename)
uploadform.csv.data.save('uploads/csvs/' + filename)
flash("CSV saved.")
return redirect(url_for('list'))
else: # <--- so this block runs
filename = None
# And your app will only render the same view as when using HTTP GET on that method
return render_template('spreadsheets/upload.html', uploadform=uploadform)
因此,如果不需要覆盖validate
方法,则只需删除它,如果是,则调整它以返回True
:
def validate(self):
"""Validate the form."""
initial_validation = super(UploadForm, self).validate()
if not initial_validation:
return False
return True # <-- this part is missing
当然你可以使用缩短版本,我觉得更合适的版本:
def validate(self):
"""Validate the form."""
initial_validation = super(UploadForm, self).validate()
return not initial_validation
答案 3 :(得分:1)
我认为有一种更简单的上传文件的方法。 这是我实施的,希望对您有所帮助。因为您当前的要求看起来与我的要求相似,所以您的解决方案看起来有些复杂。
所以我想制作一个pdf上传器页面,这就是我所做的。
UPLOAD_FOLDER = r'C:\location\app\upload'
ALLOWED_EXTENSIONS = {'pdf'}
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route("/#route details here", methods=['GET', 'POST'])
def xyz():
if request.method == 'POST':
if 'file' not in request.files:
flash(f'No file part', 'danger')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash(f'No selected file', 'danger')
return redirect(request.url)
if file and allowed_file(file.filename): #allowed file is the definition i created in point 2.
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) #save file in a target folder.
new_report = Report(report_name=filename, report_welder_wps_association_id=report_id) #create a database entry with exact filename
db.session.add(new_report)
db.session.commit()
return redirect(url_for(#redirection on success condition))
return render_template(#render template requirements go here)
@app.route('/upload/<filename>')
def uploaded_file(filename) -> object:
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
这是我需要定义的唯一形式:
class XYZ(db.Model):
__tablename__ = 'xyz'
uploaded_file_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
uploaded_file_name = db.Column(db.String(300), nullable=False)