登陆注册

说明:

  令牌Token认证,在对HTTP形式的API发请求时,大部分情况我们不是通过用户名密码做验证,而是通过一个令牌[Token来做验证]。

  RESTful API无法使用Flask-Login扩展来实现用户认证。因为其没有客户端,通过postman请求,无法设置cookie和session

  RESTful API不保存状态,无法依赖Cookie及Session来保存用户信息。需要使用Flask-HTTPAuth扩展,完成RESTful API的用户认证工作

  Flask-HTTPAuth提供了几种不同的Auth方法,比如:HTTPBasicAuth、HTTPTokenAuth、MultiAuth、HTTPDigestAuth。

  此时,我们就要使用Flask-HTTPAuth扩展中的HTTPTokenAuth对象中提供

  ”login_required”装饰器来认证视图函数、”error_handler”装饰器来处理错误、”verify_token”装饰器来验证令牌。

认证:

  两种方式:一种:包含加密过的用户数据 ;二种:不包含加密的用户数据

  1、使用flask的session,但没有使用flask-session扩展,session数据无法存储在服务器上,

    所以session的数据会被加密后保存到浏览器的cookies中。

   此种方式相当于 token 中包含加密过的用户数据 的情况。

    session['uid'] = 123

    {'uid': 123}

    sadufijklf260398riowehqklasdfnlk;d8rif2309wqeopsk

  2、使用flask-session在服务器上存储session数据,session数据不需要存储在客户端中,

    但是需要在浏览器的cookies 中存储一个session_id 的值,来标记当前请求对用的session是谁。

    session_id = 875456786sdagadh

    此种方式相当于 token 中不包含用户数据

流程:

  1、用户登录,登录成功后,服务器为此用户生成一个 token(包含代表用户身份信息的加密字符串)

    {'uid': 123} 加密成 asdfjpoqwiefojklsd09823uiowejnfy8ij239-0eopifhdv789uiohjrefd8u9ioj

    此加密字符串中也包含 时间信息,用于 token 的过期验证

  2、在登录请求的响应中,向客户端返回 上一步 生成的 token

  3、客户端再次请求服务器时,会在 请求头 中携带 token

  4、服务器从请求头中获取 token,进行解密工作,如果解密失败:

    1、不是服务器加密的数据,会解密失败

    2、超过了有效期

  5、如果解密成功,则从 token 数据中获得用户的身份信息,如:uid,可以通过 uid 获得当前登录用户的用户对象

加密解密:

  itsdangerous库提供了对信息加签名(Signature)的功能,我们可以通过它来生成并验证令牌。Flask 默认已经安装。

 from flask import Flask, g
from flask_httpauth import HTTPTokenAuth
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from itsdangerous import BadSignature, SignatureExpired auth = HTTPTokenAuth() app = Flask(__name__)
app.config['SECRET_KEY'] = ''
# 实例化了一个针对JSON的签名序列化对象token_serializer。它是有时效性的,60分钟后序列化后的签名即会失效,就无法解密
token_serializer = Serializer(app.config['SECRET_KEY'], expires_in=3600) # 参数:key ; 过期时间(秒) users = ['jack', 'tom']
for user in users:
# .dump(加密信息)对用户信息进行加密,然后生成token;.loads(要解密的加密值)对数据解密
token = token_serializer.dumps({'username': user}).decode('utf-8')
print('*** token for {}: {}\n'.format(user, token))

  

安装:

  • pip install flask-httpauth
  • 为了简化,我们将Token与用户的关系保存在一个字典中:

使用:

 import datetime
from flask import current_app
from flask_sqlalchemy import SQLAlchemy
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer db = SQLAlchemy() # 频道 1
class Channel(db.Model):
__tablename__ = "channels" id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(16), unique=True, nullable=False)
sort = db.Column(db.Integer, nullable=False)
articles = db.relationship('Article', backref='channel', lazy='dynamic') # 文章 N
class Article(db.Model):
__tablename__ = "articles" id = db.Column(db.Integer, primary_key=True)
created_at = db.Column(db.DateTime, default=datetime.datetime.now())
updated_at = db.Column(db.DateTime, default=datetime.datetime.now(), onupdate=datetime.datetime.now())
title = db.Column(db.String(256), nullable=False)
content = db.Column(db.String(5000), nullable=False) channel_id = db.Column(db.Integer, db.ForeignKey("channels.id"))
author_id = db.Column(db.Integer, db.ForeignKey('users.id')) # 用户 1
class User(db.Model):
__tablename__ = 'users' id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(32), unique=True, nullable=False)
password = db.Column(db.String(256), nullable=False)
articles = db.relationship('Article', backref='author', lazy='dynamic') # 通过类方法,生成一个加密数据赋值给token变量,进行加密。
def generate_auth_token(self, expires_in=3600):
# Serializer(key验证值密钥,过期时间) 设置key密钥和expires_in过期时间实例化Serializer。
serializer = Serializer(current_app.config['SECRET_KEY'], expires_in=expires_in)
# 通过Serializer实例化的serializer对象 把当前用户的id用序列化器进行了加密,格式为urf-8。
token = serializer.dumps({'user_id': self.id}).decode('utf-8')
return token @classmethod
def check_auth_token(cls, token):
serializer = Serializer(current_app.config['SECRET_KEY'])
try:
data = serializer.loads(token) # 从token中,解密数据
except:
return None
if 'user_id' in data:
return cls.query.get(data['user_id']) # User.query.get()
else:
return None

模型

 # 在 __init__.py 文件中

 from flask import Flask
from flask_restful import Api
from app import views, ext
from app.apis import ChannelList, ChannelDetail, ArticleList, ArticleDetail, UserRegister, UserLogin
from app.views import restful_bp def create_app():
app = Flask(__name__)
# session数据加密:from itsdangerous import TimedJSONWebSignatureSerializer
# TimedJSONWebSignatureSerializer 数据加密
# generate_password_hash,check_password_hash都会依赖app中的secret_key
app.config['SECRET_KEY'] = '' ext.init_db(app)
ext.init_migrate(app) # 注册实例化api扩展。prefix前缀名
api = Api(app, prefix='/api')
# 注册频道路由
api.add_resource(ChannelList, '/ChannelLists', endpoint='ChannelLists')
api.add_resource(ChannelDetail, '/ChannelDetails/<int:id>', endpoint='ChannelDetails')
# 注册文章路由
api.add_resource(ArticleList, '/Articles', endpoint='Articles')
api.add_resource(ArticleDetail, '/ArticleDetails/<int:id>', endpoint='ArticleDetails')
# 用户注册登陆
api.add_resource(UserRegister, '/auth/register', endpoint='user_register')
api.add_resource(UserLogin, '/auth/login', endpoint='user_login') # 蓝图
app.register_blueprint(blueprint=restful_bp)
return app

路由

 import os
from flask_httpauth import HTTPTokenAuth
from flask_migrate import Migrate
from app.models import db migrate = Migrate()
auth = HTTPTokenAuth() def init_db(app):
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(app.root_path, 'sqlite3.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app=app) def init_migrate(app):
migrate.init_app(app=app,db=db)

扩展

 import datetime

 from flask import g
from flask_restful import Resource, reqparse, fields, marshal_with, abort
from requests import auth
from werkzeug.security import generate_password_hash, check_password_hash
from app.models import Channel, db, Article, User # 输入格式化验证
user_parser = reqparse.RequestParser()
user_parser.add_argument('username', required=True, type=str)
user_parser.add_argument('password', required=True, type=str)
# 输出格式化设置
user_fields = {
'id': fields.Integer,
'username': fields.String
} # 用户注册
class UserRegister(Resource):
@marshal_with(fields=user_fields)
def post(self):
args = user_parser.parse_args()
# 需要自定义验证,验证用户名是否重复。未写
user = User()
user.username = args.get('username')
user.password = generate_password_hash(args['password']) db.session.add(user)
db.session.commit()
return user, 201

# 用户登陆
class UserLogin(Resource):
def post(self):
args = user_parser.parse_args()
user = User.query.filter_by(username=args['username']).first()
if user is None or not check_password_hash(user.password, args['password']):
return {'msg': '用户名密码错误'}, 400
token = user.generate_auth_token() # 登陆成功为该用户生成一个token认证值,返回给客户端。
return {'token': token}        # 返回给客户端保存
# token验证。装饰器验证成功则True;否则False
@auth.verify_token
def verify_token(token):
# 验证token的回调函数。如果验证成功则使用token中的用户身份信息(user_id)从数据库中查询当前登录用户数据,返回uesr对象;如果验证不成功,则返回None
user = User.check_auth_token(token)
if user is None:
return False
# 验证成功后,将当前登录用户的对象设置到g对象中,供后续使用
g.user = user
return True

  @auth.verify_token和@auth.login_required 帮助我们对生成返回给客户端的token进行沿验证。

  auth = HTTPTokenAuth()扩展初始化实例时不需要传入app对象,也不需要调用auth.init_app(app)注入应用对象

保护:

  @auth.login_required对频道模块的get请求进行保护

 # 频道模块。get、put、patch、delete
class ChannelDetail(Resource):
"""
GET /channels/123
PUT /channels/234
PATCH /channels/123
DELETE /channels/123
"""
def get_object(self,id):
channel = Channel.query.get(id)
if channel is None:
return abort(404,message="找不到对象")
return channel @auth.login_required # 对该函数的请求进行保护,token用户验证。如果该用户的token值错误或者过期,该用户将无法访问此函数
@marshal_with(fields=channel_article_fields)
def get(self,id):
channel = self.get_object(id)
return channel,200
 import datetime
from flask import g
from flask_restful import Resource, reqparse, fields, marshal_with, abort, marshal
from werkzeug.security import generate_password_hash, check_password_hash from app.ext import auth
from app.models import Channel, db, Article, User # ============================ N ===================================
# 自定义一个类,用于时间格式化
class MyDTFmt(fields.Raw):
def format(self, value):
return datetime.datetime.strftime(value, '%Y-%m-%d %H:%M:%S') # 定义参数验证格式
article_parser = reqparse.RequestParser()
article_parser.add_argument('title', required=True, type=str, help="标题必填")
article_parser.add_argument('content', required=True, type=str, help="正文必填")
article_parser.add_argument('channel_id', required=True, type=int, help="频道必填") # 定义返回输出格式
article_fields = {
'id': fields.Integer,
'url': fields.Url(endpoint='ArticleDetails', absolute=True),
'title': fields.String,
'content': fields.String,
# 等同于:'channel':fields.Nested(channel_fields),
'channel': fields.Nested({ # 通过Nested将对象解开
'name': fields.String,
'url': fields.Url(endpoint="ChannelDetails", absolute=True),
"sort": fields.Integer,
}),
"author": fields.Nested({
'id': fields.Integer,
'name': fields.String(attribute='username'),
}),
'created_at': MyDTFmt, # 进行自定义时间格式化
'updated_at': fields.DateTime(dt_format="iso8601")
} # 文章模块。get、post
class ArticleList(Resource): @auth.login_required
@marshal_with(fields=article_fields)
def get(self):
articles = Article.query.all()
return articles, 200 @auth.login_required
@marshal_with(fields=article_fields)
def post(self):
args = article_parser.parse_args() article = Article()
article.title = args.get('title')
article.content = args.get('content')
article.channel_id = args.get('channel_id')
article.author_id = g.user.id # 登陆状态下设置文章作者 db.session.add(article)
db.session.commit()
return article, 201

文章保护

验证:

  通过postman请求登陆时返回的此用户的token值,在postman中设置上此用户的token值请求登陆访问频道列表

  

补充:

  marshal的使用。api.py

 import datetime

 from flask import g
from flask_restful import Resource, reqparse, fields, marshal_with, abort, marshal
from werkzeug.security import generate_password_hash, check_password_hash from app.ext import auth
from app.models import Channel, db, Article, User # 输出格式化设置
user_fields = {
'id': fields.Integer,
'username': fields.String
} # 用户登陆
class UserLogin(Resource):
def post(self):
args = user_parser.parse_args()
user = User.query.filter_by(username=args['username']).first()
if user is None or not check_password_hash(user.password, args['password']):
return {'msg': '用户名密码错误'}, 400
token = user.generate_auth_token() # 登陆成功生成一个token为该用户
# return {'token': token} # 返回给客户端保存 ret = {
'code': 0,
'msg': '',
'data': {
# marshal相当于'user':'user.to_dict()' 将对象user与user_fileds中的同名参数进行匹配,
# marshal将user对象通过user_fields转化为字典形式传递出去
'user': marshal(user, user_fields),
'token': token,
}
}
return ret


文件上传

原生实现:在实际生产中上传大型文件,一般采用分段上传

  • 上传表单
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="Upload">
</form> {# 设置表单类型为enctype="multipart/form-data" 上传文件才可以生效#}
  • 视图函数

    通过request对象上的files"file = request.files['file']"获取文件;使用 save() 方法保存文件到指定位置

 import os
from flask import Flask, request, render_template, send_from_directory, url_for
from werkzeug.utils import secure_filename app = Flask(__name__)
# 设置上传存放文件的文件夹。uploads是此项目下的手动创建的目录。注意:手动创建
app.config['UPLOAD_FOLDER'] = os.path.join(app.root_path, 'uploads')
# 设置最大上传文件的大小值。10 M;一定要设置此值,不设置可能存在漏洞
app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024 # 判断文件的类型。原理:对文件名右边以点切割,获取脚标为1的参数,即是扩展名
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1] in {'png', 'jpg', 'jpeg', 'gif'} @app.route('/uploads/<filename>')
def uploaded_file(filename):
"""
send_from_directory,从指定的目录中(第一个参数)查找一个指定名字的文件(第二个参数)
如果找到,则将文件读入内存,并且最为响应返回给浏览器。只会在开发环境中使用
"""
return send_from_directory(app.config['UPLOAD_FOLDER'],filename) @app.route('/upload-test/', methods=['POST', 'GET'])
def upload_test():
if request.method == 'POST':
# 从表单中获得名为photo的上传文件对象,通过request.files.get获取。
# files类型是字典,一个表单中可以有多个上传文件,可以通过遍历获取多个上传文件
file = request.files.get('photo') if file:
# 首先判断文件的 扩展名 是否被允许
if allowed_file(file.filename):
# 将文件名做一个安全处理:'my movie.mp4' -> 'my_movie.mp4'
filename = secure_filename(file.filename)
# 将文件保存到指定目录(第一个参数),以某个文件名(第二个参数)
# 第二个参数是保存文件的文件名,可以自定义修改。filename = 'abc.def'
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
# 通过url_for和uploaded_file视图函数,反向解析出文件的访问地址。供前端访问
file_url = url_for('uploaded_file', filename=filename)
return render_template('upload.html', photo_url=file_url)
else:
return render_template('upload.html', err='不支持的文件类型')
return render_template('upload.html') if __name__ == '__main__':
app.run()

结合flask-wtf实现:

  • 表单验证 form
 from flask_wtf import Form
from flask_wtf.file import FileField, FileAllowed, FileRequired class PhotoForm(Form):
photo = FileField('photo', validators=[
FileRequired(), # 不能为空
# 验证文件格式
FileAllowed(['jpg', 'png', 'webp'], '只能上传图片')
])
  • 视图

  form.photo.data.filename和form.photo.data.save进行获取和保存

 import os
from flask import Flask, render_template, url_for
from werkzeug.utils import secure_filename
from forms import PhotoForm app = Flask(__name__)
# flask-wtf中生成csrf token必须依赖SECRET_KEY
app.config['SECRET_KEY'] = ''
# 设置上传文件的文件夹,在static中
app.config['UPLOAD_FOLDER'] = os.path.join(app.root_path, 'static/uploads') @app.route('/upload/', methods=('GET', 'POST'))
def upload():
form = PhotoForm()
file_url = '' if form.validate_on_submit():
# 获取photo中的数据
filename = secure_filename(form.photo.data.filename)
form.photo.data.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
file_url = url_for('static', filename='uploads/' + filename)
return render_template('upload.html', form=form, file_url=file_url) if __name__ == '__main__':
app.run()
  • 模板
 <body>
{% if form.errors %}
<div>
{{ form.errors }}
</div>
{% endif %} <form action="" method="post" enctype="multipart/form-data">
{{ form.csrf_token() }}
<input type="file" name="photo">
<input type="submit" value="Upload">
</form> <div>
photo: <img src="{{ file_url }}" alt="">
</div>
</body>

flask-uploads实现:

安装:

  • pip install flask-uploads  
  • 视图
 import os
from flask import Flask, render_template
from flask_uploads import UploadSet, configure_uploads, IMAGES, patch_request_class
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired, FileAllowed app = Flask(__name__)
app.config['SECRET_KEY'] = '' # 为 flask_uploads 创建一个上传目录
app.config['UPLOADED_PHOTOS_DEST'] = os.path.join(app.root_path, 'uploads')
# 设置上传文件类型集合。可以将IMAGES[第二个参数]文件中的文件类型上传到名字叫photos[第一个参数]的文件中
photos = UploadSet('photos', IMAGES)
configure_uploads(app, photos)
# 设置最大上传大小, 默认 64 MB
patch_request_class(app) class UploadForm(FlaskForm):
photo = FileField(validators=[
FileAllowed(photos, '只能上传图片!'),
FileRequired('文件未选择')]) @app.route('/upload/', methods=['GET', 'POST'])
def upload_file():
form = UploadForm()
if form.validate_on_submit():
# 将 文件(form.photo.data)保存到 photos 上传集合中
filename = photos.save(form.photo.data)
# 获得文件的 url
file_url = photos.url(filename)
# 实际项目中,会把文件的 url 保存到数据库中
else:
file_url = None
return render_template('upload.html', form=form, file_url=file_url) if __name__ == '__main__':
app.run()
  • 模版
 <body>
<form method="post" enctype="multipart/form-data">
{{ form.csrf_token() }}
{{ form.photo }}
{% for error in form.photo.errors %}
<p>{{ error }}</p>
{% endfor %}
<input type="submit" value="upload">
</form> {% if file_url %}
photo:<img src="{{ file_url }}">
{% endif %}
</body>

前后端的分离实现:

  目前企业中,大多数上传文件的业务会把文件存储在 云存储 中,每个项目会向云存储申请上传的 secret_key,通过 secret_key 和 云存储上传api,将用户的文件上传到云存储中,并返回文件url

  1. 前端向后端发起申请上传的请求
  2. 后端根据云存储的 secret_key 及 云存储要求的特定算法(一会会由云存储的sdk提供)计算一个包含有效期的上传令牌(token),并将此 token 返回给前端
  3. 前端拿到上传 token 后,从本地直接将文件上传到云存储服务器(上传时会一起将 token 提交),上传成功后,云存储返回 url
  4. 前端将上传后的文件url及表单中的其他信息一起提交给后端进行保存

上线部署

介绍:

  WSGI:W web、S server、G gateway、I interface。俗称:web容器,web服务器

  Nginx:也是一个服务器软件,反向代理、负载均衡;Nginx 背后可以有多个 WSGI 服务器

   Nginx 是不无论什么编程语言都可以使用的代理服务器软件。性能强悍、用户多、漏洞少、稳定性强。

  它接收用户的请求后,会转发给真正的运行python代码的服务器WSGI容器,WSGI容器内部会去执行python代码[python代码放在WSGI容器中才可以运行起来]

过程:  

  用户访问时通过Nginx,Nginx会负载均衡、反向代理到WSGI容器上。python的代码是不可以直接与Ngins交互的,Nginx是不知道如何执行python代码的,

  所以需要python代码是在WSGI SERVER容器中运行的,通过WSGI运行就可以提供http服务;执行完后会返回app响应,相应会交给Nginx。最终返回给客户端 


  WSGI,  是一个协议,pep 333/3333 规范的协议,规定了服务器如何与应用程序交互web容器,执行我们的应用程序(比如:flask + 业务代码)

  uWSGI,是一个 服务器软件,实现了 WSGI 协议,可以运行标准的 WSGI 应用程序(Flask 应用)。同时也有自己的特有协议:uwsgi

  uwsgi, 是 uWSGI 特有的协议

                         / WSGI SERVER 1     <======>    Python(Flask app)
client <-----> Nginx - WSGI SERVER 2 <======> Python(Flask app)
\ WSGI SERVER 3 <======> Python(Flask app) WSGI 就像油箱容器,我们的程序就像汽油。把程序放到这个容器中,就可以启动了 

安装:

配置:

部署:

flask 之(七) --- 认证|文件|部署的更多相关文章

  1. flask +gevent+nginx+Gunicorn+supervisor部署flask应用

    上篇   可以完美部署flask ,但是视乎在结合gevent+apscheduler 实现异步非阻塞后台和定时任务的时候视乎不是那么完美.请教了前辈,决定使用flask+gevent+nginx+g ...

  2. 一、虚拟环境.二、路由配置主页与404.三、2.x路由分发.四、伪静态.五、request对象.六、FBV与CBV.七、文件上传.

    一.虚拟环境 ''' 解决版本共存 1. 用pycharm选择File点击NewProject然后选择virtualenv创建一个纯净环境 2. 打开下载的目录将venv文件夹下的所有文件(纯净的环境 ...

  3. uWSGI+Nginx+Flask在Linux下的部署

    搞了一天多,终于搞通了uWSGI的部署原理,下面总结一下遇到的一些坑,希望给读者能够少走弯路.        简单来说,uWSGI是一个web服务器,Nginx进行反向代理的其实跟这些服务器可以说没有 ...

  4. Flask采用Virtualenv+Supervisor+Nginx部署应用

    Flask采用Virtualenv+Supervisor+Nginx部署应用 -- 首先是概念解释 WSGI服务器,负责我们的app与服务器的交互,常用的有Gunicorn Web服务器,是个HTTP ...

  5. tp 七牛云文件上传

    1.先创建好七牛云账号和存储空间 申请七牛云账号: 创建七牛云存储空间: 在账号的秘钥管理里面创建秘钥 获取AccessKey / SecretKey: 2.集成PHP-SDK 七牛云开发文档:htt ...

  6. Jenkins上War文件部署实战

    War文件部署 1.jenkins需要安装Deploy Plugin插件:在[系统管理]-[插件管理]下,如果没有安装,则在可选插件下找到该插件,然后安装(如图是1.5版本安装好的截图) 2.在构建的 ...

  7. express 热启动 静态文件部署 跨域解决 调试

    1.热启动 每次修改app.js文件,都得重新启动项目,十分不方便.这里可以用hotnode插件实现热启动 安装:$ npm install -g hotnode 启动项目:$ hotnode app ...

  8. flask控制上传文件的大小

    1.flask控制上传文件的大小的方案是全局控制:http://docs.jinkan.org/docs/flask/patterns/fileuploads.html from flask impo ...

  9. vmware vSphere client中,选择文件->部署OVF模板,报错处理方法

    在vmware vSphere client中,选择文件->部署OVF模板,选择指定的OVA文件,按步骤进行,则会出现这样的错误:此OVF软件包使用了不受支持的功能.OVF软件包需要不支持的硬件 ...

随机推荐

  1. BZOJ3157 国王奇遇记——神奇的推式子

    先膜一发Miskcoo,大佬的博客上多项式相关的非常全 原题戳我 题目大意 求 \[\sum\limits_{i=1}^{n}i^mm^i\] 题解 设一个函数\(f(i)=\sum\limits_{ ...

  2. BZOJ3331 [BeiJing2013]压力[圆方树+树上差分]

    圆方树新技能get.具体笔记见图连通性问题学习笔记. 这题求无向图的必经点,这个是一个固定套路:首先,一张连通的无向图中,每对点双和点双之间是以一个且仅一个割点连接起来的(如果超过一个就不能是割点了) ...

  3. ios的uc浏览器图片加载不出来原因

    最近做一个落地页发现一个在ios设备上uc浏览器的bug 在uc浏览器开启广告过滤的时候,会把图片过滤掉,无论是背景图还是img标签加载的图片 经过搜索与实验,发现广告过滤的设置关掉就可以,可是一般情 ...

  4. UVALive 6858——分类讨论&&水题

    题目 链接 题意:对于一个$n \times m$的矩阵的最外一圈,问是否能用$k \times 1$的方块填满 分析 考虑左右两边的情况,分类讨论,切记考虑所有可能的情形. #include< ...

  5. [2019牛客多校第二场][A. Eddy Walker]

    题目链接:https://ac.nowcoder.com/acm/contest/882/A 题目大意:圆上有\(n\)个点,标号从\(0\)到\(n-1\),初始一个人在点\(0\),每次会等概率向 ...

  6. IDEA 安装与破解(亲测有效)

    本文转载:https://blog.csdn.net/g_blue_wind/article/details/74380483 根据以下的流程,顺利安装了最新版本的idea企业版. IDEA 全称 I ...

  7. 题解 [CF916E] Jamie and Tree

    题面 解析 这题考试时刚了四个小时. 结果还是爆零了 主要就是因为\(lca\)找伪了. 我们先考虑没有操作1,那就是裸的线段树. 在换了根以后,主要就是\(lca\)不好找(分类讨论伪了). 我们将 ...

  8. php+上传超大文件

    demo下载:http://t.cn/Ai9p3CKQ 教程:http://t.cn/Aipg9uUK 一提到大文件上传,首先想到的是啥??? 没错,就是修改php.ini文件里的上传限制,那就是up ...

  9. java+大文件上传下载

    文件上传下载,与传统的方式不同,这里能够上传和下载10G以上的文件.而且支持断点续传. 通常情况下,我们在网站上面下载的时候都是单个文件下载,但是在实际的业务场景中,我们经常会遇到客户需要批量下载的场 ...

  10. 早停!? earlystopping for keras

    为了获得性能良好的神经网络,网络定型过程中需要进行许多关于所用设置(超参数)的决策.超参数之一是定型周期(epoch)的数量:亦即应当完整遍历数据集多少次(一次为一个epoch)?如果epoch数量太 ...