背景与需求:
最近二开一个Flask项目,需要上传图片,一般上传图片如果稍微大一点的公司项目会使用图片服务器,比如七牛云之类的。
但是很多公司为了省钱也会选择直接将图片让后端直接存某个电脑上。
由于是小项目,博主就选择存在了这个Flask的目录中,但是用户量(比如10-20人)也可以选用这个方案。直接让后端存在电脑上,而不是使用图片服务器。
Flask实现效果:
可以通过下方的输入来访问后端的图片
前端访问地址
http://127.0.0.1:9527/static/specialty_pic/20240519_155609/1.jpg
前端浏览器访问效果
Apipost上传图片
强烈推荐使用这个工具,我觉得比postman好用!
理由有几点,
第一postman博主也用过,保存接口还得注册账户,我们不是每时每刻都有那么方便的网络。
第二这个软件国产,国产软件某些程度上更有优势,
比如他可以团队协作、前端可以看我的接口用例,
我以前每次都是自己写完接口还得写个接口文档,这样便于团队开发。
第三他是中文,博主接触的开发者不是每个人都喜欢英文。
后端存储图片位置:
思路:
既然是后端自己存,我们肯定是使用Flask自带的那种图片上传,插件具体选择flask_uploads。而不是自己写。这个插件比如重复的图片名称,如果前端连续传两种1.png,1.png,他会自己存为1.png和1_1.png,不要重复造轮子,开发已经够麻烦了~呵呵~
源码:
我们如何配置这个插件,大家注意仔细看截图,博主不会全部贴出,因为只需要了解关键部分就够了。
app下的__init__.py[先配置这个图片插件]
import os
from flask_cors import *
from flask import Flask, render_template
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from flask_uploads import UploadSet, ALL, configure_uploads
# from flask_uploads import UploadSet, ALL, IMAGES
from app.constant import PROJECTABSPATH
from config import config
from flask_login import LoginManager
import flask_excel as excel
from flask import json
from datetime import datetime, date
from flask_login import current_user
from flask import jsonify
from functools import wraps
def permission(permission_id):
def need_permission(func):
@wraps(func)
def inner(*args, **kargs):
if not current_user.ID:
return jsonify(401, {"msg": "认证失败,无法访问系统资源"})
else:
resources = []
resourceTree = []
resources += [res for org in current_user.organizations for res in org.resources if org.resources]
resources += [res for role in current_user.roles for res in role.resources if role.resources]
# remove repeat
new_dict = dict()
for obj in resources:
if obj.ID not in new_dict:
new_dict[obj.ID] = obj
for resource in new_dict.values():
resourceTree.append(resource.PERMS)
if permission_id in resourceTree:
return func(*args, **kargs)
else:
return jsonify({'msg': '当前操作没有权限', 'code': 403})
return inner
return need_permission
JSONEncoder = json.JSONEncoder
class CustomJSONEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(obj, date):
return obj.strftime('%Y-%m-%d')
else:
return JSONEncoder.default(self, obj)
loginmanager = LoginManager()
loginmanager.session_protection = 'strong'
#loginmanager.login_view = 'base.login'
moment = Moment()
db = SQLAlchemy()
upload_pic_class = UploadSet('files', ALL)
#
# # 图片上传地址
# UPLOADED_FILES_DEST = os.path.join(PROJECTABSPATH, "app\static\specialty_pic") # 配置文件保存的目录,本参数必须设置;
# UPLOADED_FILES_ALLOW = ['.png', '.jpg'] # 配置允许的扩展名,其他的都是不允许
# UPLOADED_FILES_DENY = ['html'] # 配置不允许的扩展名
# ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg'])
#
# def allowed_file(filename):
# return '.' in filename and \
# filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
#
# files = UploadSet('files', IMAGES)
# files.extensions = tuple(ALLOWED_EXTENSIONS)
# files.allow = allowed_file
def create_app(config_name):
# app = Flask(__name__)
# app = Flask(__name__, static_folder='/app/static/specialty_pic')
app=Flask(__name__)
# CORS(app, supports_credentials=True, resources={'/*': {'origins': 'http://127.0.0.1:*'}})
CORS(app)
# 替换默认的json编码器
app.json_encoder = CustomJSONEncoder
app.config.from_object(config[config_name])
config[config_name].init_app(app)
# 配置餐厅图片上传
configure_uploads(app, upload_pic_class)
moment.init_app(app)
db.init_app(app)
loginmanager.init_app(app)
from .base import base as base_blueprint
app.register_blueprint(base_blueprint)
excel.init_excel(app)
return app
解决插件包问题
1:有个包得换一下
# from werkzeug import secure_filename, FileStorage
from werkzeug.utils import secure_filename
from werkzeug.datastructures import FileStorage
constant.py常量文件,图片存储路径和返回前端的路径
restaurant.py编写路由【具体上传图片的接口编写】
这里大家可以直接完成前面flask_uploads配置后把这个接口添加到自己代码中测试【保证前端访问就行】,一些本来需要constant.py的常量被博主写死了,为了便于测试!
@base.route('/restaurant/upload/specialty/pic', methods=['POST'])
def upload_rest_pic():
specialty_pic_list = request.files.getlist("specialty_pic_list", None)
specialty_pic_web_path_list = []
try:
for specialty_pic in specialty_pic_list:
# name = str(time.time()) + '.' + (file.filename).split('.')[-1]
name = specialty_pic.filename
pic_storage_time = datetime.now().strftime('%Y%m%d_%H%M%S')
folder = os.path.join(UPLOAD_SPECIALTY_PIC_ABS_PATH, pic_storage_time)
print(folder)
filename = upload_pic_class.save(storage=specialty_pic, folder=folder, name=name)
# img_name = filename.split('\')[-1].replace('upload', '')
img_name = filename.split('\')[-1]
# 形如这样 http://127.0.0.1:9527/static/specialty_pic/20240519_155609/1.jpg
# 240522便于大家学习测试,先写死
WEB_SPECIALITY_PIC_STORAGE_PATH = 'http://127.0.0.1:9527/static/specialty_pic/'
pic_return_path = os.path.join(WEB_SPECIALITY_PIC_STORAGE_PATH, img_name)
specialty_pic_web_path_list.append(pic_return_path)
except Exception as e:
error_info = traceback.format_exc()
print("图片上传问题为%s" % e)
# logger_error.error(error_info)
return ResponseJson(RetCode.PIC_UPLOAD_FAILURE, data='')
returnDict = dict()
returnDict["specialty_pic_web_path_list"] = specialty_pic_web_path_list
return jsonify({'msg': '操作成功', 'code': 200, 'data': returnDict})
# 240522便于大家学习测试,先写死,不使用封装的返回工具类
# return ResponseJson(RetCode.SUCCESS, data=returnDict)
Apipost上传图片测试
返回结果
{
"code": 200,
"data": {
"specialty_pic_web_path_list": [
"http://127.0.0.1:9527/static/specialty_pic/20240522_220814/1.jpg",
"http://127.0.0.1:9527/static/specialty_pic/20240522_220814/1_1.jpg"
]
},
"msg": "操作成功"
}
前端http://127.0.0.1:9527/static/XXX/1.png方式访问
可能问题与解决问题的参考文章!
下面文章具体讲了原始Flask的static目录和指定static目录的关系,如果你没有解决,这几篇文章应当能解决你的问题,当时博主嫌麻烦直接删除了原始的static目录,这样他就只能检索整个项目内唯一的static目录了!
flask 根目录问题(访问静态文件的方法) – 凯宾斯基 – 博客园 (cnblogs.com)
博主更新动力:
欢迎大家点赞、收藏、关注、评论、批评啦