phpstrom 激活码
RPBLK4D08G-eyJsaWNlbnNlSWQiOiJSUEJMSzREMDhHIiwibGljZW5zZWVOYW1lIjoi5o6I5p2D5Luj55CG5ZWGIGh0dHA6Ly9pZGVhLmhrLmNuIiwiYXNzaWduZWVOYW1lIjoiIiwiYXNzaWduZWVFbWFpbCI6IiIsImxpY2Vuc2VSZXN0cmljdGlvbiI6IiIsImNoZWNrQ29uY3VycmVudFVzZSI6ZmFsc2UsInByb2R1Y3RzIjpbeyJjb2RlIjoiSUkiLCJmYWxsYmFja0RhdGUiOiIyMDE5LTA1LTA1IiwicGFpZFVwVG8iOiIyMDIwLTA1LTA0In0seyJjb2RlIjoiQUMiLCJmYWxsYmFja0RhdGUiOiIyMDE5LTA1LTA1IiwicGFpZFVwVG8iOiIyMDIwLTA1LTA0In0seyJjb2RlIjoiRFBOIiwiZmFsbGJhY2tEYXRlIjoiMjAxOS0wNS0wNSIsInBhaWRVcFRvIjoiMjAyMC0wNS0wNCJ9LHsiY29kZSI6IlBTIiwiZmFsbGJhY2tEYXRlIjoiMjAxOS0wNS0wNSIsInBhaWRVcFRvIjoiMjAyMC0wNS0wNCJ9LHsiY29kZSI6IkdPIiwiZmFsbGJhY2tEYXRlIjoiMjAxOS0wNS0wNSIsInBhaWRVcFRvIjoiMjAyMC0wNS0wNCJ9LHsiY29kZSI6IkRNIiwiZmFsbGJhY2tEYXRlIjoiMjAxOS0wNS0wNSIsInBhaWRVcFRvIjoiMjAyMC0wNS0wNCJ9LHsiY29kZSI6IkNMIiwiZmFsbGJhY2tEYXRlIjoiMjAxOS0wNS0wNSIsInBhaWRVcFRvIjoiMjAyMC0wNS0wNCJ9LHsiY29kZSI6IlJTMCIsImZhbGxiYWNrRGF0ZSI6IjIwMTktMDUtMDUiLCJwYWlkVXBUbyI6IjIwMjAtMDUtMDQifSx7ImNvZGUiOiJSQyIsImZhbGxiYWNrRGF0ZSI6IjIwMTktMDUtMDUiLCJwYWlkVXBUbyI6IjIwMjAtMDUtMDQifSx7ImNvZGUiOiJSRCIsImZhbGxiYWNrRGF0ZSI6IjIwMTktMDUtMDUiLCJwYWlkVXBUbyI6IjIwMjAtMDUtMDQifSx7ImNvZGUiOiJQQyIsImZhbGxiYWNrRGF0ZSI6IjIwMTktMDUtMDUiLCJwYWlkVXBUbyI6IjIwMjAtMDUtMDQifSx7ImNvZGUiOiJSTSIsImZhbGxiYWNrRGF0ZSI6IjIwMTktMDUtMDUiLCJwYWlkVXBUbyI6IjIwMjAtMDUtMDQifSx7ImNvZGUiOiJXUyIsImZhbGxiYWNrRGF0ZSI6IjIwMTktMDUtMDUiLCJwYWlkVXBUbyI6IjIwMjAtMDUtMDQifSx7ImNvZGUiOiJEQiIsImZhbGxiYWNrRGF0ZSI6IjIwMTktMDUtMDUiLCJwYWlkVXBUbyI6IjIwMjAtMDUtMDQifSx7ImNvZGUiOiJEQyIsImZhbGxiYWNrRGF0ZSI6IjIwMTktMDUtMDUiLCJwYWlkVXBUbyI6IjIwMjAtMDUtMDQifSx7ImNvZGUiOiJSU1UiLCJmYWxsYmFja0RhdGUiOiIyMDE5LTA1LTA1IiwicGFpZFVwVG8iOiIyMDIwLTA1LTA0In1dLCJoYXNoIjoiMTI5NDQ5NjcvMCIsImdyYWNlUGVyaW9kRGF5cyI6NywiYXV0b1Byb2xvbmdhdGVkIjpmYWxzZSwiaXNBdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlfQ==-PkRZyGH6nrZ0ae6q/etTxz0Cv7xR4Sx9XB3wQyrW1D/5TrKjbDKl+70N1+AJOvm+ppQEdXds11Ux6Jks9ckVz2y/jlwG2dmiRtV0pjqUOKIYDL5EGsW6qHktdDKnwFj3l7QrrZ5AXDniG1Rhf1PVVA5CXjF8o+Q537E2xVxy0ZJohD2T8PiHBaoWr5LUnAOFstnfcGyoeX8jSi8qSw9vDIY3ncB6TVk9ms371TT3kYOTpcN3DO2V3sghppl5SrJbJTU/5HIMUnnUcVQaoN34ak7/m5btDH8BB0li/jFUMbDbdVh9E/x6lWb7jvfZN9Z8VEM0UCCdqYmkiJoxbeDUrg==-MIIElTCCAn2gAwIBAgIBCTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTE4MTEwMTEyMjk0NloXDTIwMTEwMjEyMjk0NlowaDELMAkGA1UEBhMCQ1oxDjAMBgNVBAgMBU51c2xlMQ8wDQYDVQQHDAZQcmFndWUxGTAXBgNVBAoMEEpldEJyYWlucyBzLnIuby4xHTAbBgNVBAMMFHByb2QzeS1mcm9tLTIwMTgxMTAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxcQkq+zdxlR2mmRYBPzGbUNdMN6OaXiXzxIWtMEkrJMO/5oUfQJbLLuMSMK0QHFmaI37WShyxZcfRCidwXjot4zmNBKnlyHodDij/78TmVqFl8nOeD5+07B8VEaIu7c3E1N+e1doC6wht4I4+IEmtsPAdoaj5WCQVQbrI8KeT8M9VcBIWX7fD0fhexfg3ZRt0xqwMcXGNp3DdJHiO0rCdU+Itv7EmtnSVq9jBG1usMSFvMowR25mju2JcPFp1+I4ZI+FqgR8gyG8oiNDyNEoAbsR3lOpI7grUYSvkB/xVy/VoklPCK2h0f0GJxFjnye8NT1PAywoyl7RmiAVRE/EKwIDAQABo4GZMIGWMAkGA1UdEwQCMAAwHQYDVR0OBBYEFGEpG9oZGcfLMGNBkY7SgHiMGgTcMEgGA1UdIwRBMD+AFKOetkhnQhI2Qb1t4Lm0oFKLl/GzoRykGjAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBggkA0myxg7KDeeEwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBCwUAA4ICAQAF8uc+YJOHHwOFcPzmbjcxNDuGoOUIP+2h1R75Lecswb7ru2LWWSUMtXVKQzChLNPn/72W0k+oI056tgiwuG7M49LXp4zQVlQnFmWU1wwGvVhq5R63Rpjx1zjGUhcXgayu7+9zMUW596Lbomsg8qVve6euqsrFicYkIIuUu4zYPndJwfe0YkS5nY72SHnNdbPhEnN8wcB2Kz+OIG0lih3yz5EqFhld03bGp222ZQCIghCTVL6QBNadGsiN/lWLl4JdR3lJkZzlpFdiHijoVRdWeSWqM4y0t23c92HXKrgppoSV18XMxrWVdoSM3nuMHwxGhFyde05OdDtLpCv+jlWf5REAHHA201pAU6bJSZINyHDUTB+Beo28rRXSwSh3OUIvYwKNVeoBY+KwOJ7WnuTCUq1meE6GkKc4D/cXmgpOyW/1SmBz3XjVIi/zprZ0zf3qH5mkphtg6ksjKgKjmx1cXfZAAX6wcDBNaCL+Ortep1Dh8xDUbqbBVNBL4jbiL3i3xsfNiyJgaZ5sX7i8tmStEpLbPwvHcByuf59qJhV/bZOl8KqJBETCDJcY6O2aqhTUy+9x93ThKs1GKrRPePrWPluud7ttlgtRveit/pcBrnQcXOl1rHq7ByB8CFAxNotRUYL9IF5n3wJOgkPojMy6jetQA5Ogc8Sm7RG6vg1yow==
————————————————
版权声明:本文为CSDN博主「书山有路_邓」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41497075/article/details/88853773
Multiple request sequences that represent a logically related session should be executed with the same HttpContext instance to ensure automatic propagation of conversation context and state information between requests.
上面这段话摘自httpclient官网,大体意思是逻辑会话相关的多个请求序列应该使用同一个HttpContext实例,这样就可以让会话信息和状态信息在多个请求之间自动广播。
官网上还给出一段示例代码 ,我们仿着它的示例代码,重新整一个,以便于观察。
(1) 使用springboot快迅搭建一个目标服务
@RestController
public class RequestController {
@PostMapping("/request")
public void request(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession();
//1. 从session中获取username
String username = (String) session.getAttribute("username");
String ret;
if (username == null) {
// 2. 从请求参数中获取username的值
username = request.getParameter("username");
session.setAttribute("username", username);
ret = "login success!";
} else {
ret = "Having been logined " + username;
}
// 将ret 内容写回到response响应体中
ServletOutputStream outputStream = null;
PrintWriter pw = null;
try {
outputStream = response.getOutputStream();
pw = new PrintWriter(outputStream);
pw.write(ret);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
启功类省略,server.port = 9999
(2) HttpClient测试类
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class HttpContextTest {
public static void main(String[] args) throws IOException {
HttpContext httpContext = new BasicHttpContext();
HttpClientContext httpClientContext = HttpClientContext.adapt(httpContext);
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost("http://localhost:9999/request");
// 模仿form表单请求,设置请求参数
List<NameValuePair> nvp = new ArrayList<>();
nvp.add(new BasicNameValuePair("username", "admin"));
// 第一次请求时,设置请求参数
httpPost.setEntity(new UrlEncodedFormEntity(nvp));
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httpPost, httpClientContext);
HttpEntity entity = response.getEntity();
if (entity != null) {
String ret = EntityUtils.toString(entity);
System.out.println("第一次请求响应:"+ ret);
}
}finally {
response.close();
}
System.out.println("=================第二次请求====================");
// 重新创建一个HttpPost对象,但是此次该对象中不设置请求参数
httpPost = new HttpPost("http://localhost:9999/request");
try {
response = httpclient.execute(httpPost, httpClientContext);
HttpEntity entity = response.getEntity();
if (entity != null) {
String ret = EntityUtils.toString(entity);
System.out.println("第二次请求响应:"+ ret);
}
}finally {
response.close();
}
}
}
(3)启动目标项目,然后再运行测试代码,打印结果如下
第一次请求响应:login success!
=================第二次请求====================
第二次请求响应:Having been logined admin
Process finished with exit code 0
感觉浏览器请求一模一样了, 保存了请求会话信息,事实上确实如此,此处就是保存jsessionid
(4) 简单看下源码
<1> ProtocolExec#execute()
<2> ResponseProcessCookies#process(final HttpResponse response, final HttpContext context)
@Override
public void process(final HttpResponse response, final HttpContext context)
throws HttpException, IOException {
final HttpClientContext clientContext = HttpClientContext.adapt(context);
// Obtain actual CookieSpec instance
final CookieSpec cookieSpec = clientContext.getCookieSpec();
// Obtain cookie store
final CookieStore cookieStore = clientContext.getCookieStore();
//.......
// see if the cookie spec supports cookie versioning.
if (cookieSpec.getVersion() > 0) {
// process set-cookie2 headers.
// Cookie2 will replace equivalent Cookie instances
// 就是将cookie信息拷到cookieStore中
it = response.headerIterator(SM.SET_COOKIE2);
processCookies(it, cookieSpec, cookieOrigin, cookieStore);
}
}
<3> 断点查看第二次请求时HttpContext对象
通过debug可以很明显看到,HttpContext将诸多的Http请求的会话信息进行了广播。
在之前在工作中遇到在富文本编辑器中粘贴图片不能展示的问题,于是各种网上扒拉,终于找到解决方案,在这里感谢一下知乎中众大神以及TheViper。
通过知乎提供的思路找到粘贴的原理,通过TheViper找到粘贴图片的方法。
其原理为一下步骤:
监听粘贴事件;【用于插入图片】
获取光标位置;【记录图片插入位置】
获取剪切板内容;【主要是获取文件】
上传剪切板图片;
在指定光标位置插入图片。
以下是代码部分:
1.获取光标代码部分,大部分都是直接利用TheViper的代码,只是做了简单的修改,在获取光标的位置添加了插件子集document对象,因为直接使用document对象获取不到光标位置
var isSupportRange = typeof document.createRange == ‘function‘;
var currentRange,
_parentElement;
// 获取当前光标多在位置
function getCurrentRange(target) {
var selection,
range;
if (isSupportRange) {
selection = target.getSelection();
if (selection.getRangeAt && selection.rangeCount) {
range = selection.getRangeAt(0);
_parentElement = range.commonAncestorContainer;
}
} else {
range = target.selection.createRange();
_parentElement = range.parentElement();
}
return range;
}
function saveSelection(target) {
currentRange = getCurrentRange(target);
}
function _restoreSelection() {
if (!currentRange) {
return;
}
var selection,
range;
if (isSupportRange) {
selection = document.getSelection();
selection.removeAllRanges();
selection.addRange(currentRange);
} else {
range = document.selection.createRange();
range.setEndPoint(‘EndToEnd‘, currentRange);
if (currentRange.text.length === 0) {
range.collapse(false);
} else {
range.setEndPoint(‘StartToStart‘, currentRange);
}
range.select();
}
}
function insertImage(html,target) {
if (document.selection)
currentRange.pasteHTML(html);
else
target.execCommand("insertImage", false, html);
saveSelection();
}
2.监听粘贴事件、获取上传图片、上传至服务器并添加至编辑器
CKEDITOR.instances[‘document-info‘].on(‘instanceReady‘, function(e) {
this.document.on(‘paste‘, function(event) {
var target = event.sender.$;
saveSelection(target);
var items = event.data.$.clipboardData.items;
if (!items) {
return;
}
for (var i = items.length - 1; i >= 0; i--) {
if (items[i].kind == ‘file‘ && items[i].type.indexOf(‘image/‘) !== -1) {
var file = items[i].getAsFile();
if (file) {
if (file.size === 0) {
return;
}
var formData = new FormData();
formData.append("file", file);
$.ajax({
method: ‘POST‘,
url: url,
data: formData,
processData: false,
contentType: false,
success: function(response) {
var _img_html = response.url;
insertImage(_img_html,target);
}
});
}
}
}
});
});
优化后的代码变得更加精简
//手动粘贴
this.PasteManual = function ()
{
if (!this.setuped)
{
this.setup_tip(); return;
}
if (!this.chrome45 && !_this.edge)
{
this.app.paste();
}
elseif (this.chrome45)
{
this.app.paste();
}
elseif(this.edge)
{
this.app.paste();
}
};
前台的引用也非常的简单:
<!DOCTYPEhtmlPUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<htmlxmlns="http://www.w3.org/1999/xhtml">
<head>
<metahttp-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>WordPaster-jsp-ueditor-1.2.6.0</title>
<scripttype="text/javascript" src="ueditor.config.js" charset="utf-8"></script>
<scripttype="text/javascript" src="ueditor.all.min.js" charset="utf-8"></script>
<linktype="text/css" rel="Stylesheet" href="WordPaster/css/WordPaster.css"/>
<linktype="text/css" rel="Stylesheet" href="WordPaster/js/skygqbox.css" />
<scripttype="text/javascript" src="WordPaster/js/json2.min.js" charset="utf-8"></script>
<scripttype="text/javascript" src="WordPaster/js/jquery-1.4.min.js" charset="utf-8"></script>
<scripttype="text/javascript" src="WordPaster/js/w.edge.js" charset="utf-8"></script>
<scripttype="text/javascript" src="WordPaster/js/w.app.js" charset="utf-8"></script>
<scripttype="text/javascript" src="WordPaster/js/w.file.js" charset="utf-8"></script>
<scripttype="text/javascript" src="WordPaster/js/skygqbox.js" charset="utf-8"></script>
<scripttype="text/javascript" src="WordPaster/js/WordPaster.js" charset="utf-8"></script>
</head>
<body>
<textareaname="后台取值的key"id="myEditor">这里写你的初始化内容</textarea>
<scripttype="text/javascript">
var pasterMgr = new WordPasterManager();
pasterMgr.Config["PostUrl"] = "http://localhost:8080/WordPaster2UEditor1.4x/upload.jsp"
pasterMgr.Load();//加载控件
var ue = UE.getEditor(‘myEditor‘);
ue.ready(function() {
//设置编辑器的内容
ue.setContent(‘hello‘);
//获取html内容,返回: <p>hello</p>
var html = ue.getContent();
//获取纯文本内容,返回: hello
var txt = ue.getContentTxt();
pasterMgr.SetEditor(ue);
});
</script>
</body>
</html>
数据提交部分需要注意
processData: false,
contentType: false,
这两项需要设置,否则文件不能正常上传
该问题没有完美的解决,存在以下疑问,如有想法,请告知。
1.从word中复制图片为rtf格式,不能被保存,并解析图片,有待解决;
2.只能单个文件复制,多个文件复制存在问题。这个问题使用WordPaster插件解决掉了,能够批量上传Word中的所有图片,并且保留Word样式,效果如下:
服务器能够接收到图片,并进行保存
编辑器中的图片地址已经全部被替换成了服务器的图片地址,其它的用户也能够正常访问
在接触该问题前期,错误的解决思路:
1.通过input控件上传,因浏览器安全设置原因,不允许input:file赋值;
2.使用convas将图片转换为base64存储,该处也有问题,传唤base64时,存在跨域问题。
经过这些处理基本上实现了一个完整的Word图片上传插件(WordPaster),能够自动上传剪切板中的图片,能够自动上传Word中的所有图片,使用起来非常的方便,有需要的朋友可以直接去官网下载使用:
Django的配置:
pycharm中创建django工程之后注释掉MIDDLEWARE项中的‘django.middleware.csrf.CsrfViewMiddleware‘。此处作用是为了能够让js获取到cookie值
同时为了使用mysql,我们在setting中修改DATABASES中的‘default‘项为
‘ENGINE‘: ‘django.db.backends.mysql‘,
‘NAME‘: ‘db_test_shop1‘,
‘HOST‘:‘127.0.0.1‘,
‘PORT‘:3306,
‘USER‘:‘root‘,
‘PASSWORD‘:‘1415926‘,
参数一目了然无需多讲
在django的控制台下执行python manage.py startapp 你的app项目名来新建一个app项目,此后的工程都在这里写。然后在setting中的INSTALLED_APPS项中注册app,添加‘django.apps.你的app的名字Config‘
同时在setting中添加以下代码来寻找static路径和模板代码路径
STATICFILES_DIRS=[
os.path.join(BASE_DIR,‘static‘)
]
开启调试功能的代码为在setting中添加
LOGGING = {
‘version‘: 1,
‘disable_existing_loggers‘: False,
‘handlers‘: {
‘console‘:{
‘level‘:‘DEBUG‘,
‘class‘:‘logging.StreamHandler‘,
},
},
‘loggers‘: {
‘django.db.backends‘: {
‘handlers‘: [‘console‘],
‘propagate‘: True,
‘level‘:‘DEBUG‘,
},
}
}
为了使用现成的数据库中的数据,我们可以通过在控制台中执行python manage.py inspectdb来获取代码,复制粘贴到app中的model文件中
html模板中若为了使用django的模板语言需要在开头加上{% load static %}
Django中后端的编写
以上准备工作完成之后在app项目文件夹里的urls里如下配置
from django.contrib import admin
from django.urls import path
from tableapp.views import *
urlpatterns = [
path(‘admin/‘, admin.site.urls),
path(‘‘,indexRender_CookieAndSession),
path(‘test‘,testAjax_CookieAndSession),
path(‘goTestRender‘,goTestRender),
path(‘getSession‘,getSession),
]
path的第一个参数为前端的请求地址,第二个参数为调用的函数
然后就在view文件里编写函数即可
from django.shortcuts import render
from tableapp.models import *
from django.views.static import HttpResponse
from django.http import JsonResponse
from tableapp.models import *
import json
from django.views.decorators.csrf import csrf_exempt
# Create your views here.
def getSession(request):
return JsonResponse({"result":request.session.get(json.loads(request.body).get("key"))})
def indexRender_CookieAndSession(request):
#测试session
request.session["t1_session"]="session1"
#删除session: del request.session["t1_session"]
#网页转发到index.html
rsp = render(request,"index.html")
#测试cookie设置
rsp.set_cookie("t1_cookie",233)
#删除cookie: rsp.delete_cookie("t1_cookie")
return rsp
def goTestRender(request):
return render(request,"test.html")
# @csrf_exempt
def testAjax_CookieAndSession(request):
#request是json格式传过来的参数,需要转换成字典对象
params = json.loads(request.body)
print("testParamInteger:",params["testParamInteger"])
print("testParamString:", params["testParamString"])
# 测试session
request.session["t2_session"] = "session2"
#orm简单的两表查询
# 用模型类.objects来进行数据库操作,filter等同于where操作,values_list将查询结果转换成结果元祖,而非values的字典对组成的json对象形式。可以通过切片进行选取操作,[开始位置:结束位置:隔几个取一个](以上参数和切片一样可以任意省略)
data = TGoods.objects.filter(goods_status=1).values_list("goods_id","goods_name","goods_price","type__t_g_type__type_name")[0:10:1]
title = ("商品ID","商品名","商品价格","商品类别","操作")
result = {"result":1,"data":data,"title":title}
#这里可以替换成 return JsonResponse(result)
resp = HttpResponse(json.dumps(result),content_type="application/json")
resp.set_cookie("t2_cookie","cookie2")
return resp
然后提前放上测试用的各种代码
<!DOCTYPE html>
<html lang="en">
<head>
{% load static %}
<meta charset="UTF-8">
<script type="text/javascript" src="https://www.gxlsystem.com/{% static ‘js/jquery-3.2.1.min.js‘ %}"></script>
<script type="text/javascript" src="https://www.gxlsystem.com/{% static ‘js/jquery-cookie.js‘ %}"></script>
<script type="text/javascript" src="https://www.gxlsystem.com/{% static ‘js/my-session.js‘ %}"></script>
<title>Title</title>
</head>
<body>
<a href="https://www.gxlsystem.com/goTestRender">跳转到test页面</a>
</body>
<script type="text/javascript">
console.log("render cookie:",$.cookie("t1_cookie"))
console.log("render session:",getSession("t1_session"))
</script>
</html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
{% load static %}
<title>test table</title>
{# <link rel="stylesheet" href="https://www.gxlsystem.com/style.css">#}
<!-- Delete ".min" for console warnings in development -->
</head>
<body>
<!-- 取消这个范围内的django解析,从而解决django与vue语法冲突问题 -->
{% verbatim %}
<script type="text/x-template" id="my_test_template">
<table>
<thead>
<tr>
<td v-for="t in title">
{{ t }}
</td>
</tr>
</thead>
<tbody>
<tr v-for="row in testData">
<td v-for="d in row">{{ d }}</td>
</tr>
</tbody>
</table>
</script>
{% endverbatim %}
<!-- Vue.compent里的变量名=实例化时对应的变量名 -->
<!-- Vue.compent里的变量名如果是驼峰命名两个单词的话需要用中划线分割 -->
<my-test-table
:title="myTitle"
:test-data="myData"
></my-test-table>
</body>
<script type="text/javascript" src="https://www.gxlsystem.com/{% static ‘js/jquery-3.2.1.min.js‘ %}"></script>
<script type="text/javascript" src="https://www.gxlsystem.com/{% static ‘js/jquery-cookie.js‘ %}"></script>
<script type="text/javascript" src="https://www.gxlsystem.com/{% static ‘js/my-session.js‘ %}"></script>
<script type="text/javascript" src="https://www.gxlsystem.com/{% static ‘js/vue.js‘ %}"></script>
<script type="text/javascript" src="https://www.gxlsystem.com/{% static ‘js/table.js‘ %}"></script>
</html>
test.html
function getSession(key) {
result = undefined
$.ajax({
url:"getSession",
type:"post",
dataType:"json",
data:JSON.stringify({key:key}),
async:false,
success:function (data) {
result = data.result
}
})
return result
}
my-session.js
Vue.component(‘my-test-table‘,{
template:"#my_test_template",
props:{
title:Array,
testData:Array,
}
})
function getData()
{
$.ajax({
url:"test",
type:"post",
dataType:"json",
data:JSON.stringify({
testParamInteger:1,
testParamString:"测试参数"
}),
success:function (data) {
console.log(data)
// 传过来的参数自动被解析成对象了,无需转换
var myVue = new Vue({
el:"my-test-table",
data:{
myTitle:data.title,
myData:data.data
}
})
// 测试cookie和session
console.log("Ajax Cookie:"+$.cookie("t2_cookie"))
console.log("Ajax Session:"+getSession("t2_session"))
}
})
}
$(function () {
getData()
})
table.js
测试结果
难点解释:
前端的my-session.js中的async:flase是为了取消异步,能让返回值在获取到结果之后再返回
cookie获取方法为引入jquery-cookie.js文件,调用$.cookie即可
session的获取方法为my-session中再次请求
此外其他点都在注释里了
//-------------------------------------------------------------------
basic_fields.js
init: function () {
this._setCurrency();
//-------------------------------------------------------------------
_setCurrency: function () {
var currencyField = this.nodeOptions.currency_field || this.field.currency_field || ‘currency_id‘;
var currencyID = this.record.data[currencyField] && this.record.data[currencyField].res_id;
this.currency = session.get_currency(currencyID);
this.formatOptions.currency = this.currency;
//-------------------------------------------------------------------
core/session.js
get_currency: function (currency_id) {
return this.currencies[currency_id];
},
//-------------------------------------------------------------------
service/session.js
odoo.define(‘web.session‘, function (require) {
"use strict";
var Session = require(‘web.Session‘);
var modules = odoo._modules;
var session = new Session(undefined, undefined, {modules: modules, use_cors: false});
session.is_bound = session.session_bind();
return session;
});
//-------------------------------------------------------------------
在session_bind()中调用this.session_init()
在this.session_init()中调用this.session_reload()
session_reload: function () {
var result = _.extend({}, window.odoo.session_info);
delete result.session_id;
_.extend(this, result);
return $.when();
}
在运行到session_reload时session_info中已经拿到了res_currency的币种信息
wfk!!!
再往底层就搞不明时什么意思了:
window.odoo是什么玩意?
window 又是什么?
interface Window extends EventTarget, WindowTimers, WindowSessionStorage, WindowLocalStorage, WindowConsole, GlobalEventHandlers, IDBEnvironment, WindowBase64, GlobalFetch, WindowOrWorkerGlobalScope, WindowEventHandlers {
//-------------------------------------------------------------------
1:控制两个相邻边盒子之间的距离,在A或者B盒子上用margin控制,就可以控制距离了。
2:父子级之间的元素,控制父元素A的padding部分,就可以控制距离了。
在前面的章节中,我们从纯理论的角度对依赖注入进行了深入论述,我们接下来会对.NET Core依赖注入框架进行单独介绍。为了让读者朋友能够更好地理解.NET Core依赖注入框架的设计与实现,我们按照类似的原理创建了一个简易版本的依赖注入框架,也就是我们在前面多次提及的Cat。
源代码下载
一、编程体验
虽然我们对这个名为Cat的依赖注入框架进行了最大限度的简化,但是与.NET Core框架内部使用的真实依赖注入框架相比,Cat不仅采用了一致的设计,而且几乎具备了后者所有的功能特性。为了让大家对Cat具有一个感官的认识,我们先来演示一下如何利用它来提供我们所需的服务实例。
作为依赖注入容器的Cat对象不仅仅作为服务实例的提供者,它同时还需要维护着服务实例的生命周期。Cat提供了三种生命周期模式,如果要了解它们之间的差异,就必须对多个Cat之间的层次关系有充分的认识。一个代表依赖注入容器的Cat对象用来创建其他的Cat对象,后者视前者为“父容器”,所以多个Cat对象通过其“父子关系”维系一个树形层次化结构。不过这仅仅是一个逻辑结构而已,实际上每个Cat对象只会按照下图所示的方式引用整棵树的根。
在了解了多个Cat对象之间的关系之后,对于三种预定义的生命周期模式就很好理解了。如下所示的Lifetime枚举代表着三种生命周期模式,其中Transient代表容器针对每次服务请求都会创建一个新的服务实例,而Self则是将提供服务实例保存在当前容器中,它代表针对某个容器范围内的单例模式,Root则是将每个容器提供的服务实例统一存放到根容器中,所以该模式能够在多个“同根”容器范围内确保提供的服务是单例的。
public enum Lifetime
{
Root,
Self,
Transient
}
代表依赖注入容器的Cat对象之所以能够为我们提供所需服务实例,其根本前提是相应的服务注册在此之前已经添加到容器之中。服务总是针对服务类型(接口、抽象类或者具体类型)进行注册,Cat通过定义的扩展方法提供了如下三种注册方式。除了直接提供服务实例的形式外(默认采用Root模式),我们在注册服务的时候必须指定一个具体的生命周期模式。
指定具体的实现类型。
提供一个服务实例。
指定一个创建服务实例的工厂。
我们定义了如下的接口和对应的实现类型来演示针对Cat的服务注册。其中Foo、Bar、Baz和Gux分别实现了对应的接口IFoo、IBar、IBaz和IGux,其中Gux类型上标注了一个MapToAttribute特性注册了与对应接口IGux之间的映射。为了反映Cat对服务实例生命周期的控制,我们让它们派生于同一个基类Base。Base实现了IDisposable接口,我们在其构造函数和实现的Dispose方法中输出相应的文本以确定对应的实例何时被创建和释放。我们还定义了一个泛型的接口IFoobar<T1, T2>和对应的实现类Foobar<T1, T2>来演示Cat针对泛型服务实例的提供。
public interface IFoo {}
public interface IBar {}
public interface IBaz {}
public interface IGux {}
public interface IFoobar<T1, T2> {}
public class Base : IDisposable
{
public Base() => Console.WriteLine($"Instance of {GetType().Name} is created.");
public void Dispose() => Console.WriteLine($"Instance of {GetType().Name} is disposed.");
}
public class Foo : Base, IFoo{ }
public class Bar : Base, IBar{ }
public class Baz : Base, IBaz{ }
[MapTo(typeof(IGux), Lifetime.Root)]
public class Gux : Base, IGux { }
public class Foobar<T1, T2>: IFoobar<T1,T2>
{
public IFoo Foo { get; }
public IBar Bar { get; }
public Foobar(IFoo foo, IBar bar)
{
Foo = foo;
Bar = bar;
}
}
在如下所示的代码片段中我们创建了一个Cat对象并采用上面提到的方式针对接口IFoo、IBar和IBaz注册了对应的服务,它们采用的生命周期模式分别为Transient、Self和Root。然后我们还调用了另一个将当前入口程序集作为参数的Register方法,该方法会解析指定程序集中标注了MapToAttribute特性的类型并作相应的服务注册,对于我们演示的程序来,该方法会完成针对IGux/Gux类型的服务注册。接下来我们利用Cat对象创建了它的两个子容器,并调用子容器的GetService<T>方法提供相应的服务实例。
class Program
{
static void Main()
{
var root = new Cat()
.Register<IFoo, Foo>(Lifetime.Transient)
.Register<IBar>(_=> new Bar(), Lifetime.Self)
.Register<IBaz, Baz>(Lifetime.Root)
.Register(Assembly.GetEntryAssembly());
var cat1 = root.CreateChild();
var cat2 = root.CreateChild();
void GetServices<TService>(Cat cat)
{
cat.GetService<TService>();
cat.GetService<TService>();
}
GetServices<IFoo>(cat1);
GetServices<IBar>(cat1);
GetServices<IBaz>(cat1);
GetServices<IGux>(cat1);
Console.WriteLine();
GetServices<IFoo>(cat2);
GetServices<IBar>(cat2);
GetServices<IBaz>(cat2);
GetServices<IGux>(cat2);
}
}
上面的程序运行之后会在控制台上输出如图3-7所示的结果,输出的内容不仅表明Cat能够根据添加的服务注册提供对应类型的服务实例,还体现了它对生命周期的控制。由于服务IFoo被注册为Transient服务,所以Cat针对该接口的服务提供四次请求都会创建一个全新的Foo对象。IBar服务的生命周期模式为Self,如果我们利用同一个Cat对象来提供对应的服务实例,该Cat对象只会创建一个Bar对象,所以整个过程中会创建两个Bar对象。IBaz和IGux服务采用Root生命周期,所以具有同根的两个Cat对象提供的总是同一个Baz/Gux对象,后者只会被创建一次。
除了提供类似于IFoo、IBar和IBaz这样非泛型的服务实例之外,如果具有针对泛型定义(Generic Definition)的服务注册,Cat同样也能提供泛型服务实例。如下面的代码片段所示,在为创建的Cat对象添加了针对IFoo和IBar接口的服务注册之后,我们调用Register方法注册了针对泛型定义IFoobar<,>的服务注册,具体的实现类型为Foobar<,>。当我们利用Cat对象提供一个类型为IFoobar<IFoo, IBar>的服务实例的时候,它会创建并返回一个Foobar<Foo, Bar>对象。
public class Program
{
public static void Main()
{
var cat = new Cat()
.Register<IFoo, Foo>(Lifetime.Transient)
.Register<IBar, Bar>(Lifetime.Transient)
.Register(typeof(IFoobar<,>), typeof(Foobar<,>), Lifetime.Transient);
var foobar = (Foobar<IFoo, IBar>)cat.GetService<IFoobar<IFoo, IBar>>();
Debug.Assert(foobar.Foo is Foo);
Debug.Assert(foobar.Bar is Bar);
}
}
当我们在进行服务注册的时候,可以为同一个类型添加多个服务注册。虽然添加的所有服务注册均是有效的,不过由于扩展方法GetService<TService>总是返回一个唯一的服务实例,我们对该方法采用了“后来居上”的策略,即总是采用最近添加的服务注册来创建服务实例。如果我们调用另一个扩展方法GetServices<TService>,它将利用返回根据所有服务注册提供的服务实例。
如下面的代码片段所示,我们为创建的Cat对象添加了三个针对Base类型的服务注册,对应的实现类型分别为Foo、Bar和Baz。我们最后将Base作为泛型参数调用了GetServices<Base>方法,该方法会返回包含三个Base对象的集合,集合元素的类型分别为Foo、Bar和Baz。
public class Program
{
public static void Main()
{
var services = new Cat()
.Register<Base, Foo>(Lifetime.Transient)
.Register<Base, Bar>(Lifetime.Transient)
.Register<Base, Baz>(Lifetime.Transient)
.GetServices<Base>();
Debug.Assert(services.OfType<Foo>().Any());
Debug.Assert(services.OfType<Bar>().Any());
Debug.Assert(services.OfType<Baz>().Any());
}
}
如果提供的服务实例实现了IDisposable接口,我们应该在适当的时候调用其Dispose方法释放该服务实例。由于服务实例的生命周期完全由作为依赖注入容器的Cat对象来管理,那么通过调用Dispose方法来释放服务实例自然也应该由它来负责。Cat针对提供服务实例的释放策略取决于采用的生命周期模式,具体的策略如下:Transient和Self:所有实现了IDisposable接口的服务实例会被当前Cat对象保存起来,当Cat对象自身的Dispose方法被调用的时候,这些服务实例的Dispose方法会随之被调用。Root:由于服务实例保存在作为根容器的Cat对象上,所以当这个Cat对象的Dispose方法被调用的时候,这些服务实例的Dispose方法会随之被调用。
上述的释放策略可以通过如下的演示实例来印证。我们在如下的代码片段中创建了一个Cat对象,并添加了相应的服务注册。我们接下来调用了CreateChild方法创建代表子容器的Cat对象,并用它提供了四个注册服务对应的实例。
class Program
{
static void Main()
{
using (var root = new Cat()
.Register<IFoo, Foo>(Lifetime.Transient)
.Register<IBar>(_ => new Bar(), Lifetime.Self)
.Register<IBaz, Baz>(Lifetime.Root)
.Register(Assembly.GetEntryAssembly()))
{
using (var cat = root.CreateChild())
{
cat.GetService<IFoo>();
cat.GetService<IBar>();
cat.GetService<IBaz>();
cat.GetService<IGux>();
Console.WriteLine("Child cat is disposed.");
}
Console.WriteLine("Root cat is disposed.");
}
}
}
由于两个Cat对象的创建都是在using块中进行的,所以它们的Dispose方法都会在using块结束的地方被调用。为了确定方法被调用的时机,我们特意在控制台上打印了相应的文字。该程序运行之后会在控制台上输出如下图所示的结果,我们可以看到当作为子容器的Cat对象的Dispose方法被调用的时候,由它提供的两个生命周期模式分别为Transient和Self的两个服务实例(Foo和Bar)被正常释放了。至于生命周期模式为Root的服务实例Baz和Gux,它的Dispose方法会延迟到作为根容器的Cat对象的Dispose方法被调用的时候。
二、设计与实现
在完成针对Cat的编程体验之后,我们来聊聊这个依赖注入容器的设计原理和具体实现。由于作为依赖注入容器的Cat对象总是利用预先添加的服务注册来提供对应的服务实例,所以服务注册至关重要。如下所示的就是表示服务注册的ServiceRegistry的定义,它具有三个核心属性(ServiceType、Lifetime和Factory),分别代表服务类型、生命周期模式和用来创建服务实例的工厂。最终用来创建服务实例的工厂体现为一个类型为Func<Cat,Type[], object>的委托对象,它的两个输入分别代表当前使用的Cat对象以及提供服务类型的泛型参数,如果提供的服务类型并不是一个泛型类型,这个参数被会指定为一个空的数组。
public class ServiceRegistry
{
public Type ServiceType { get; }
public Lifetime Lifetime { get; }
public Func<Cat,Type[], object> actory { get; }
internal ServiceRegistry Next { get; set; }
public ServiceRegistry(Type serviceType, Lifetime lifetime, Func<Cat,Type[], object> factory)
{
ServiceType = serviceType;
Lifetime = lifetime;
Factory = factory;
}
internal IEnumerable<ServiceRegistry> AsEnumerable()
{
var list = new List<ServiceRegistry>();
for (var self = this; self!=null; self= self.Next)
{
list.Add(self);
}
return list;
}
}
我们将针对同一个服务类型(ServiceType属性相同)的多个ServiceRegistry组成一个链表,作为相邻节点的两个ServiceRegistry对象通过Next属性关联起来。我们为ServiceRegistry定义了一个AsEnumerable方法使它返回由当前以及后续节点组成的ServiceRegistry集合。如果当前ServiceRegistry为链表头,那么这个方法会返回链表上的所有ServiceRegistry对象。下图体现了服务注册核心三要素和链表结构。
在了解了表示服务注册的ServiceRegistry之后,我们来着重介绍表示依赖注入容器的Cat类型。如下面的代码片段所示,Cat同时实现了IServiceProvider和IDisposable接口,定义在前者中的GetService方法用于提供服务实例。作为根容器的Cat对象通过公共构造函数创建,另一个内部构造函数则用来创建作为子容器的Cat对象,指定的Cat对象将作为父容器。
public class Cat : IServiceProvider, IDisposable
{
internal readonly Cat _root;
internal readonly ConcurrentDictionary<Type, ServiceRegistry> _registries;
private readonly ConcurrentDictionary<Key, object> _services;
private readonly ConcurrentBag<IDisposable> _disposables;
private volatile bool _disposed;
public Cat()
{
_registries = new ConcurrentDictionary<Type, ServiceRegistry>();
_root = this;
_services = new ConcurrentDictionary<Key, object>();
_disposables = new ConcurrentBag<IDisposable>();
}
internal Cat(Cat parent)
{
_root = parent._root;
_registries = _root._registries;
_services = new ConcurrentDictionary<Key, object>();
_disposables = new ConcurrentBag<IDisposable>();
}
private void EnsureNotDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException("Cat");
}
}
...
}
作为根容器的Cat对象通过_root字段表示。_registries字段返回的ConcurrentDictionary<Type, ServiceRegistry>对象用来存储所有添加的服务注册,该字典对象的Key和Value分别表示服务类型和ServiceRegistry链表,下图体现这一映射关系。由于需要负责完成对提供服务实例的释放工作,所以我们需要将实现了IDisposable接口的服务实例保存在通过_disposables字段表示的集合中。
由当前Cat对象提供的非Transient服务实例保存在由_services字段表示的一个ConcurrentDictionary<Key, object>对象上,该字典对象的键类型为如下所示的Key,它相当于是创建服务实例所使用的ServiceRegistry对象和泛型参数类型数组的组合。
internal class Key : IEquatable<Key>
{
public ServiceRegistry Registry { get; }
public Type[] GenericArguments { get; }
public Key(ServiceRegistry registry, Type[] genericArguments)
{
Registry = registry;
GenericArguments = genericArguments;
}
public bool Equals(Key other)
{
if (Registry != other.Registry)
{
return false;
}
if (GenericArguments.Length != other.GenericArguments.Length)
{
return false;
}
for (int index = 0; index < GenericArguments.Length; index++)
{
if (GenericArguments[index] != other.GenericArguments[index])
{
return false;
}
}
return true;
}
public override int GetHashCode()
{
var hashCode = Registry.GetHashCode();
for (int index = 0; index < GenericArguments.Length; index++)
{
hashCode ^= GenericArguments[index].GetHashCode();
}
return hashCode;
}
public override bool Equals(object obj) => obj is Key key ? Equals(key) : false;
}
虽然我们为Cat定义了若干扩展方法来提供多种不同的服务注册,但是这些方法最终都会调用如下这个Register方法,该方法会将提供的ServiceRegistry添加到_registries字段表示的字典对象中。值得注意的是,不论我们是调用哪个Cat对象的Register方法,指定的ServiceRegistry都会被添加到作为根容器的Cat对象上。
public class Cat : IServiceProvider, IDisposable
{
public Cat Register(ServiceRegistry registry)
{
EnsureNotDisposed();
if (_registries.TryGetValue(registry.ServiceType, out var existing))
{
_registries[registry.ServiceType] = registry;
registry.Next = existing;
}
else
{
_registries[registry.ServiceType] = registry;
}
return this;
}
...
}
用来提供服务实例的核心操作实现在如下这个GetServiceCore方法中。如下面的代码片段所示,我们在调用该方法的时候需要指定对应的ServiceRegistry对象的服务类型的泛型参数。当该方法被执行的时候,对于Transient的生命周期模式,它会直接利用ServiceRegistry提供的工厂来创建服务实例。如果服务实例的类型实现了IDisposable接口,该对象会被添加到_disposables字段表示的待释放服务实例列表中。对于Root和Self生命周期模式,该方法会先根据提供的ServiceRegistry判断是否对应的服务实例已经存在,存在的服务实例会直接返回。
public class Cat : IServiceProvider, IDisposable
{
private object GetServiceCore(ServiceRegistry registry, Type[] genericArguments)
{
var key = new Key(registry, genericArguments);
var serviceType = registry.ServiceType;
switch (registry.Lifetime)
{
case Lifetime.Root: return GetOrCreate(_root._services, _root._disposables);
case Lifetime.Self: return GetOrCreate(_services, _disposables);
default:
{
var service = registry.Factory(this, genericArguments);
if (service is IDisposable disposable && disposable != this)
{
_disposables.Add(disposable);
}
return service;
}
}
object GetOrCreate(ConcurrentDictionary<Key, object> services, ConcurrentBag<IDisposable> disposables)
{
if (services.TryGetValue(key, out var service))
{
return service;
}
service = registry.Factory(this, genericArguments);
services[key] = service;
if (service is IDisposable disposable)
{
disposables.Add(disposable);
}
return service;
}
}
}
GetServiceCore方法只有在指定ServiceRegistry对应的服务实例不存在的情况下才会利用提供的工厂来创建服务实例,创建的服务实例会根据生命周期模式保存到作为根容器的Cat对象或者当前Cat对象上。如果提供的服务实例实现了IDisposable接口,在采用Root生命周期模式下会被保存到作为根容器的Cat对象的待释放列表中。如果生命周期模式为Self,它会被添加到当前Cat对象的待释放列表中。
在实现的GetService方法中,Cat会根据指定的服务类型找到对应的ServiceRegistry对象,并最终调用GetServiceCore方法来提供对应的服务实例。GetService方法还会解决一些特殊服务的提供问题,比如若服务类型为Cat或者IServiceProvider,该方法返回的就是它自己。如果服务类型为IEnumerable<T>,GetService方法会根据泛型参数类型T找到所有的ServiceRegistry并利用它们来创建对应的服务实例,最终返回的是由这些服务实例组成的集合。除了这些,针对泛型服务实例的提供也是在这个方法中解决的。
public class Cat : IServiceProvider, IDisposable
{
public object GetService(Type serviceType)
{
EnsureNotDisposed();
if (serviceType == typeof(Cat) || serviceType == typeof(IServiceProvider))
{
return this;
}
ServiceRegistry registry;
//IEnumerable<T>
if (serviceType.IsGenericType && serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
var elementType = serviceType.GetGenericArguments()[0];
if (!_registries.TryGetValue(elementType, out registry))
{
return Array.CreateInstance(elementType, 0);
}
var registries = registry.AsEnumerable();
var services = registries.Select(it => GetServiceCore(it, Type.EmptyTypes)).ToArray();
Array array = Array.CreateInstance(elementType, services.Length);
services.CopyTo(array, 0);
return array;
}
//Generic
if (serviceType.IsGenericType && !_registries.ContainsKey(serviceType))
{
var definition = serviceType.GetGenericTypeDefinition();
return _registries.TryGetValue(definition, out registry)
? GetServiceCore(registry, serviceType.GetGenericArguments())
: null;
}
//Normal
return _registries.TryGetValue(serviceType, out registry)
? GetServiceCore(registry, new Type[0])
: null;
}
...
}
在实现的Dispose方法中,由于所有待释放的服务实例已经保存到_disposables字段表示的集合中,所以我们只需要依次调用它们的Dispose方法即可。在释放了所有服务实例并清空待释放列表后,Dispose还会清空_services字段表示的服务实例列表。
public class Cat : IServiceProvider, IDisposable
{
public void Dispose()
{
_disposed = true;
foreach(var disposable in _disposables)
{
disposable.Dispose();
}
_disposables.Clear();
_services.Clear();
}
...
}
三、扩展方法
为了方便注册服务,我们定义了如下六个Register扩展方法。由于服务注册的添加总是需要调用Cat自身的Register方法来完成,所以这些方法最终都需要创建一个代表服务注册的ServiceRegistry对象。对于一个ServiceRegistry对象来说,它最为核心的元素莫过于表示服务实例创建工厂的Func<Cat,Type[], object>对象,所以上述这六个扩展方法需要解决的就是创建这么一个委托对象。
public static class CatExtensions
{
public static Cat Register(this Cat cat, Type from, Type to, Lifetime lifetime)
{
Func<Cat, Type[], object> factory = (_, arguments) => Create(_, to, arguments);
cat.Register(new ServiceRegistry(from, lifetime, factory));
return cat;
}
public static Cat Register<TFrom, TTo>(this Cat cat, Lifetime lifetime) where TTo:TFrom
=> cat. Register(typeof(TFrom), typeof(TTo), lifetime);
public static Cat Register(this Cat cat, Type serviceType, object instance)
{
Func<Cat, Type[], object> factory = (_, arguments) => instance;
cat.Register(new ServiceRegistry(serviceType, Lifetime.Root, factory));
return cat;
}
public static Cat Register<TService>(this Cat cat, TService instance)
{
Func<Cat, Type[], object> factory = (_, arguments) => instance;
cat.Register(new ServiceRegistry(typeof(TService), Lifetime.Root, factory));
return cat;
}
public static Cat Register(this Cat cat, Type serviceType,
Func<Cat, object> factory, Lifetime lifetime)
{
cat.Register(new ServiceRegistry(serviceType, lifetime, (_, arguments) => factory(_)));
return cat;
}
public static Cat Register<TService>(this Cat cat,
Func<Cat,TService> factory, Lifetime lifetime)
{
cat.Register(new ServiceRegistry(typeof(TService), lifetime, (_,arguments)=>factory(_)));
return cat;
}
private static object Create(Cat cat, Type type, Type[] genericArguments)
{
if (genericArguments.Length > 0)
{
type = type.MakeGenericType(genericArguments);
}
var constructors = type.GetConstructors();
if (constructors.Length == 0)
{
throw new InvalidOperationException($"Cannot create the instance of
{type} which does not have a public constructor.");
}
var constructor = constructors.FirstOrDefault(it => it.GetCustomAttributes(false).OfType<InjectionAttribute>().Any());
constructor ??= constructors.First();
var parameters = constructor.GetParameters();
if (parameters.Length == 0)
{
return Activator.CreateInstance(type);
}
var arguments = new object[parameters.Length];
for (int index = 0; index < arguments.Length; index++)
{
arguments[index] = cat.GetService(parameters[index].ParameterType);
}
return constructor.Invoke(arguments);
}
}
由于前两个重载指定的是服务实现类型,所以我们需要调用对应的构造函数来创建服务实例,这一逻辑实现在私有的Create方法中。第三个扩展方法指定的直接就是服务实例,所以我们很容易将提供的参数转换成一个Func<Cat,Type[], object>。
我们刻意简化了构造函数的筛选逻辑。为了解决构造函数的选择问题,我们引入如下这个InjectionAttribute特性。我们将所有公共实例构造函数作为候选的构造函数,并会优先选择标注了该特性的构造函数。当构造函数被选择出来后,我们需要通过分析其参数类型并利用Cat对象来提供具体的参数值,这实际上是一个递归的过程。最终我们将针对构造函数的调用转换成Func<Cat,Type[], object>对象,进而创建出表示服务注册的ServiceRegistry对象。
[AttributeUsage( AttributeTargets.Constructor)]
public class InjectionAttribute: Attribute {}
前面给出的代码片段还提供了HasRegistry和HasRegistry<T>方法来确定指定类型的服务注册是否存在。除此之外,用于提供服务实例的泛型方法GetService<T>和用于提供所有指定类型服务实例的GetServices<TService>方法采用了如下的定义方式。
public static class CatExtensions
{
public static IEnumerable<TService> GetServices<T>(this Cat cat) => cat.GetService<IEnumerable<TService >>();
public static TService GetService<TService >(this Cat cat) => (TService)cat.GetService(typeof(T));
}
上述六个扩展方法帮助我们完成针对单一服务的注册,有时间我们的项目中可能会出现非常多的服务需要注册,如何能够完成针对它们的批量注册会是不错的选择。我们的依赖注入框架提供了针对程序集范围的批量服务注册。为了标识带注册的服务,我们需要在服务实现类型上标注如下这个MapToAttribute类型,并指定服务类型(一般为它实现的接口或者继承的基类)和生命周期。
[AttributeUsage( AttributeTargets.Class, AllowMultiple = true)]
public sealed class MapToAttribute: Attribute
{
public Type ServiceType { get; }
public Lifetime Lifetime { get; }
public MapToAttribute(Type serviceType, Lifetime lifetime)
{
ServiceType = serviceType;
Lifetime = lifetime;
}
}
针对程序集范围的批量服务注册实现在Cat的如下这个Register扩展方法中。如下面的代码片段所示,该方法会从指定程序集中获取所有标注了MapToAttribute特性的类型,并提取出服务类型、实现类型和生命周期模型,然后利用它们批量完成所需的服务注册。
public static class CatExtensions
{
public static Cat Register(this Cat cat, Assembly assembly)
{
var typedAttributes = from type in assembly.GetExportedTypes()
let attribute = type.GetCustomAttribute<MapToAttribute>()
where attribute != null
select new { ServiceType = type, Attribute = attribute };
foreach (var typedAttribute in typedAttributes)
{
cat.Register(typedAttribute.Attribute.ServiceType,
typedAttribute.ServiceType, typedAttribute.Attribute.Lifetime);
}
return cat;
}
}
除了上述这七个用来注册服务的Register扩展方法,我们还为Cat类型定义了如下两个扩展方法,其中CreateService<T>方法以泛型参数的形式指定获取服务实例对应注册的类型,CreateServices<T>方法会提供指定服务类型的所有实例,而CreateChild方法则帮助我们创建一个代表子容器的Cat对象。
public static class CatExtensions
{
public static T GetService<T>(this Cat cat) => (T)cat.GetService(typeof(T));
public static IEnumerable<T> GetServices<T>(this Cat cat) => cat.GetService<IEnumerable<T>>();
public static Cat CreateChild(this Cat cat) => new Cat(cat);
}
端口号 443 和 80 端口的区别
一般指定 443 和 80 端口都是使用域名时所需要的
当我们使用域名请求时,一般是不添加端口号的
例如:
在不添加端口号的情况下,会有默认端口号的
https 默认端口号为 443
http 默认端口号为 80
同理:
ws 默认端口号为 443
wss 默认端口号为 80
Django基础之jQuery操作
jquery之cookie操作
定义:让网站服务器把少量数据储存到客户端的硬盘或内存,从客户端的硬盘读取数据的一种技术;
下载与引入:jquery.cookie.js基于jquery;先引入jquery,再引入:jquery.cookie.js;下载:
1.添加一个"会话cookie"
这里没有指明 cookie有效时间,所创建的cookie有效期默认到用户关闭浏览器为止,所以被称为 “会话cookie(session cookie)”。
2.创建一个cookie并设置有效时间为 7天
这里指明了cookie有效时间,所创建的cookie被称为“持久 cookie (persistent cookie)”。注意单位是:天;
3.创建一个cookie并设置 cookie的有效路径
在默认情况下,只有设置 cookie的网页才能读取该 cookie。如果想让一个页面读取另一个页面设置的cookie,必须设置cookie的路径。cookie的路径用于设置能够读取 cookie的顶级目录。将这个路径设置为网站的根目录,可以让所有网页都能互相读取 cookie (一般不要这样设置,防止出现冲突)。
4.读取cookie
5.删除cookie
6.可选参数
参数
有个需求,客户想做个打开官网自动播放一段视频,楼主使用了video标签,即下面的代码::
于是我在video标签上添加了属性 autoplay=“autoplay” loop=“loop”
然而通过地址栏进去的时候,视频并没有自动播放,最后,找资料发现,添加 muted 属性,就可以通过地址栏进入网页的时候自动播放了。
<video id="video" src="https://www.gxlsystem.com/xxxx/xxx.mp4" autoplay="autoplay" loop="loop" muted="muted" height="100%" width="100%"></video>
<div class="chanppin_content" id="listsBox"></div>
<a id="moreClick">查看更多</a>
<script type="text/javascript">
var page = 1;
var pageCount = 0;
loadMore();
function loadMore(){
$.getJSON(‘{:U("loadMore")}‘,{p:page}, function(d){
pageCount = d.pageCount;
$.each(d.lists, function(k, v){
$(‘#listsBox‘).append(‘<div class="chanppin_content_li wow zoomIn animated" data-wow-duration="1s" data-wow-delay=".2s"> <a href="https://www.gxlsystem.com/‘+v.url+‘"> <div class="img"> <img src="https://www.gxlsystem.com/‘+v.pic_text+‘" > </div> <div class="text"> <p>‘+v.title+‘</p> </div> </a> </div>‘);
});
});
}
$(‘#moreClick‘).click(function(){
if(page< pageCount){
page++;
loadMore();
}else{
alert(‘没有了‘);
}
});
</script>
function loadMore(){
$where = ‘`id` >0‘;
if(I(‘get.cid‘)){
$where .= ‘ and `cat_id` = ‘.I(‘get.cid‘);
}
$count = M(‘Xiangce‘)->where($where)->count();
$pageSize = 6;
$pageCount = ceil($count/$pageSize);
$page = new ThinkPage($count,$pageSize);
$page->setConfig(‘theme‘, "%UP_PAGE% %LINK_PAGE% %DOWN_PAGE% %END% <label class=‘f_14 clr_6‘>共%TOTAL_ROW%条</label>");
$this->page = $page->show();
$lists = M(‘Xiangce‘)->where($where)->order(‘order_id desc,id desc‘)->limit($page->firstRow.‘,‘.$page->listRows)->select();
foreach($lists as $k=>$v){
$lists[$k][‘url‘] = U(‘Product/view‘, [‘id‘=>$v[‘id‘]]);
$lists[$k][‘pic_text‘] = __ROOT__.‘/Attached/‘.$v[‘pic‘];
}
die(json_encode([‘lists‘=>$lists, ‘pageCount‘=>$pageCount]));
}
1.数组的find方法还是不会用,改为filter
2.正规表达式还是理解的不好
//var myrouter = require("https://www.gxlsystem.com/myrouter");
//myrouter.getRouteValue(‘login‘)(1,2);
pathname = ‘/login‘
pathname = pathname.replace(///, ""); //替换掉前面的/
console.log(‘/‘); //输出/
console.log(‘/‘); ////输出/
console.log(pathname);
// 语法: 依据 两侧一个//作为模式界定 /pattern/attributes
// 解析: 两侧的/ /,为模式界定符; 中间的/表示/,也就是/login中的/
var myrouter_action = [];
myrouter_action.push({ xpath: ‘/login‘, yvalue: function(req, res) {
res.write("我是登录方法");
console.log(101);
}});
myrouter_action.push({ xpath: ‘/register‘, yvalue: function(req, res) {
res.write("我是注册方法");
console.log(102);
}});
myrouter_action.push({ xpath: ‘/logout‘, yvalue: function(req, res) {
res.write("我是注销方法");
console.log(103);
}});
myrouter_action.push({ xpath: ‘/‘, yvalue: function(req, res) {
res.write("我是根目录方法");
console.log(100);
}});
/* 从arr中寻找标志为opath的函数 */
function getRouteValue(opath) {
var filterArray = myrouter_action.filter(function(v) {
return v.xpath === opath
})
if (filterArray.length) {
return filterArray[0].yvalue
}else{
console.log(‘查找路由表失败!‘, opath);
}
}
module.exports={
getRouteValue
}
var http = require("http");
var url = require("url");
var myrouter = require("https://www.gxlsystem.com/myrouter");
http
.createServer(function(request, response) {
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
if (request.url !== "/favicon.ico") {
console.log("1:",request.url);
var pathname = url.parse(request.url).pathname; //得到请求的路径
console.log("2:",pathname);
//pathname = pathname.replace(///, ""); //替换掉前面的/
//console.log("2",pathname);
//myrouter.getRouteValue(pathname)(request, response);
myrouter.getRouteValue(pathname)(request, response);
response.end("");
}
})
.listen(8000);
console.log("Server running at http://127.0.0.1:8000/");
//console.log(myrouter.getRouteValue(‘login‘)(1,2));
前言
工业互联网,物联网,可视化等名词在我们现在信息化的大背景下已经是耳熟能详,日常生活的交通,出行,吃穿等可能都可以用信息化的方式来为我们表达,在传统的可视化监控领域,一般都是基于 Web SCADA 的前端技术来实现 2D 可视化监控,本系统采用 Hightopo 的 HT for Web 产品来构造轻量化的 3D 可视化场景,该 3D 场景从正面展示了一个地铁站的现实场景,包括地铁的实时运行情况,地铁上下行情况,视频监控,烟雾报警,电梯运行情况等等,帮助我们直观的了解当前的地铁站。
系统中为了帮助用户更直观友好的浏览当前地铁站,提供了三种交互模式:
第一人称模式 -- 操作就类似行人或车在行进的效果,可以通过键盘鼠标控制前进后退。
自动巡检模式 -- 该模式下用户不需要任何操作,场景自动前进后退来巡查当前地铁站的场景。
鼠标操作模式 -- 左键旋转场景,右键平移场景。
本篇文章通过对地铁站可视化场景的搭建,动画代码的实现,交互模式的原理解析,以及主要功能点的实现进行阐述,帮助我们了解如何使用 实现一个简单的地铁站可视化。
预览地址:
界面简介及效果预览
地铁运行效果
地铁从站外开到站内的效果为透明度逐渐增加,速度逐渐降低。
漫游效果
上述为自动巡检的漫游效果,场景自动进行前进旋转。
监控设备交互效果
当我们点击场景中的监控设备时可以查看当前设备的运行情况,运行数据等信息。
场景搭建
该系统中的大部分模型都是通过 3dMax 建模生成的,该建模工具可以导出 obj 与 mtl 文件,在 HT 中可以通过解析 obj 与 mtl 文件来生成 3d 场景中的所有复杂模型,当然如果是某些简单的模型可以直接使用 HT 来绘制,这样会比 obj 模型更轻量化,所以大部分简单的模型都是采用 HT for Web 产品轻量化 HTML5/WebGL 建模的方案,具体的解析代码如下:
1 // 分别为 obj 文件地址,mtl 文件地址
2 ht.Default.loadObj(‘obj/metro.obj‘, ‘obj/metro.mtl‘, {
3 center: true,
4 // 模型是否居中,默认为 false,设置为 true 则会移动模型位置使其内容居中
5 r3: [0, -Math.PI / 2, 0],
6 // 旋转变化参数,格式为 [rx, ry, rz]
7 s3: [0.15, 0.15, 0.15],
8 // 大小变化参数,格式为 [sx, sy, sz]
9 finishFunc: function(modelMap, array, rawS3) {
10 if (modelMap) {
11 ht.Default.setShape3dModel(‘metro‘, array); // 注册一个名字为 metro 的模型
12 }
13 }
14 });
上面通过加载 obj 模型之后注册了一个名字为 metro 的模型,之后如果要使用该模型可以通过以下代码来实现:
1 var node = new ht.Node();
2 node.s({
3 ‘shape3d‘: ‘metro‘
4 });
上面代码新建了一个 node 对象,通过设置 style 对象的 shape3d 属性可以把模型名称为 metro 用到该 node 对象上去,之后便是我们场景中看到的地铁列车模型。
动画代码分析
地铁动画代码的实现分析
场景中地铁的运行是通过 HT 提供的调度插件来实现,调度的具体用法可以参考 HT for Web 的,该调度主要用于在指定的时间间隔进行函数回调处理,回调函数的第一个参数为 data 图元,也就是 3D 场景中的模型节点,我们可以判断当前 data 是否为我们刚才创建的 metro 那个节点来进行后续的操作,场景中模拟了一个左开的地铁和一个右开的地铁,两辆地铁会交替出现。在 3D 场景中肯定会有坐标系,HT 中是用 x, y, z 来分别表示三个轴,所以地铁的运动肯定是改变地铁在坐标系中的位置来实现地铁的运行,地铁坐标如下图所示:
通过上图可以知道地铁在 3D 场景中的坐标系,如果要实现地铁的移动则只需要将地铁往图中所示红色箭头的方向进行移动,即 x 轴的方向,通过 setX 这个方法不断的修改地铁的位置达到地铁行进的目的,代码中通过 getSpeedByX 以及 getOpacityByX 两个方法来不断获取此时的列车速度以及列车透明度,以下为关键代码实现:
1 let metroTask = {
2 interval: 50,
3 // 每五十秒执行一次
4 action: (data) = >{ // 即上文所提回调函数
5 // 判断当时传进来的节点是否为地铁列车节点
6 if (data === currentMetro) {
7 // 获取地铁此时的 X 轴位置以及行进的方向
8 let currentX = data.getX(),
9 direction = data.a(‘direction‘);
10 // 根据当前的 X 轴位置获取当前的列车速度
11 let speed = this.getSpeedByX(currentX);
12 // 根据当前的 X 轴位置获取当前的列车透明度
13 let opacity = this.getOpacityByX(currentX);
14 // 判断此时 X 轴位置是否超过某个值 即地铁是在某个范围内移动
15 if (Math.abs(currentX) <= 5000) {
16 // 设置当前的透明度
17 opacity !== 1 ? currentMetro.s({
18 ‘shape3d.transparent‘: true,
19 ‘shape3d.opacity‘: opacity
20 }) : currentMetro.s({
21 ‘shape3d.transparent‘: false
22 });
23 // 设置当前的 X 轴位置
24 data.setX(currentX + direction * speed);
25 // 判断此时地铁的速度为 0,所以此时应该执行开门的动画
26 if (speed === 0) this.doorAnimation(currentMetro, direction);
27 }
28 // 右方向地铁开到头,进行复位
29 if (currentX > 5000 && direction === 1) {
30 currentMetro = leftMetro;
31 currentMetro.setX(5000);
32 }
33 // 左方向地铁开到头,进行复位
34 if (currentX < -5000 && direction === -1) {
35 currentMetro = rightMetro;
36 currentMetro.setX( - 5000);
37 }
38 }
39 }
40 };
41 dm3d.addScheduleTask(metroTask);
通过以上代码可以知道地铁在运行的过程中,主要通过修改地铁的 x 轴位置来产生前进的动画,并且需要让地铁在某个区间内进行运动,需要判断边界,而且为了模拟出真实的效果需要根据地铁当前的位置不断获取当前的列车速度以及列车透明度,以下为流程图:
上图所示的为地铁进站时候的流程,当地铁停靠完毕关门后需要进行出站,此时我们只需要把地铁位置重新设置一下不为 0 即可,以下为部分代码实现:
1 currentMetro.setX(direction * 10); // 设置出站列车的位置
当执行上面那句代码之后上方的 metroTask 调度任务执行到 getSpeedByX 这个方法之后获取到的 speed 速度不为 0,因此此时会继续执行地铁行进的动画,此时的速度就是由慢至快,透明度由深至浅。以下为开门动画执行流程:
自动巡检代码的实现分析
系统中自动巡检的实现主要是通过修改 3D 场景中的 eye 以及 center 的值,HT 中提供了 rotate,walk 两个方法来控制视角的旋转以及视角的行进,rotate 方法在非第一人称模式时,旋转是以 center 为中心进行旋转,也就是围绕中心物体旋转,当为第一人称时旋转以 eye 为中心进行旋转,也就是旋转眼睛朝向方向。walk 函数同时改变 eye 和 center 的位置,也就是 eye 和 center 在两点建立的矢量方向上同时移动相同的偏移量。该系统中我没有采用 rotate 函数而是自己实现了视角的旋转,因为原本的 rotate 函数旋转某个角度会马上旋转过去而不会有一个旋转的过程,所以我重新实现了旋转的方法,该系统中视角旋转是通过不断修改 center 的数值来实现,具体实现过程原理如下图所示:
部分实现代码如下:
1 rotateStep() {
2 // 即上图辅助点 C
3 let fromCenter = this.fromCenter;
4 // 即上图 B 点
5 let toCenter = this.toCenter;
6 // 每帧转一度
7 let rotateValue = this.rotateFrame || Math.PI / 180;
8 // 辅助点 C 与 B 点之间建立一个方向向量
9 let centerVector = new ht.Math.Vector2(toCenter.x - fromCenter.x, toCenter.y - fromCenter.y);
10 let centerVectorLength = centerVector.length();
11 // 此时旋转百分比
12 let rotatePercent = rotateValue * this.stepNum / this.curRotateVal;
13 if (rotatePercent >= 1) {
14 rotatePercent = 1;
15 this.stepNum = -2;
16 }
17 let newLength = rotatePercent * centerVectorLength;
18 centerVector.setLength(newLength);
19 let newCenterVector = centerVector.add(fromCenter);
20 // 获取旋转过程中 center 的点信息
21 let newCenterPosition = [newCenterVector.x, this.personHeight, newCenterVector.y];
22 // 设置当前 center 的大小
23 this.g3d.setCenter(newCenterPosition);
24 }
通过上述代码就实现了场景中的视角旋转,并且可以通过修改 rotateValue 的值控制旋转的速度。
电梯动画代码的实现分析
场景中电梯是一个 obj 模型,3D 模型是由最基础的三角形面拼接合成,例如 1 个矩形可以由 2 个三角形构成,1 个立方体由 6 个面即 12 个三角形构成,以此类推更复杂的模型可以由许多的小三角形组合合成。因此 3D 模型定义即为对构造模型的所有三角形的描述,而每个三角形由三个顶点 vertex 构成,每个顶点 vertex 由 x, y, z 三维空间坐标决定,HT 中使用 vs 数组记录构成三角面的所有顶点坐标,所以如果想要让电梯运行起来,只需要把所有的顶点坐标往电梯运行的方向进行平移,以下为部分关键伪代码:
1 // vs 指的是构成电梯模型所有的三角面顶点坐标数组
2 // 由于场景中电梯的运行方向为往对角线右上方运动,所以只需要修改 x 轴以及 y 轴坐标值
3 // xStep yStep 为每次电梯运动的距离
4 setInterval(() = >{
5 // i+3 是因为 vs 数组的顺序为 x, y, z 轴 所以每次 i 偏移三个单位大小
6 for (let i = 0, l = vs.length; i < l; i = i + 3) {
7 // 该顶点坐标下一个 x 轴坐标的值
8 let nextX = vs[i] - xStep;
9 // 该顶点坐标下一个 y 轴坐标的值
10 let nextY = vs[i + 1] + yStep;
11 vs[i] = nextX < -0.5 ? 0.5 - (Math.abs(nextX) - 0.5) : nextX;
12 vs[i + 1] = nextY > 0.5 ? -0.5 + (Math.abs(nextY) - 0.5) : nextY;
13 }
14 },
15 200);
电梯运动动画如下图所示:
监控功能展示及介绍
视频监控
当点击场景中的摄像头之后右侧顶部会显示出当前摄像头的监控画面,以下为实现效果图:
烟雾报警监控
烟雾报警会根据后台实时传递过来的状态值来变换当前烟雾报警模型的颜色,红色为报警状态,以下为实现效果图:
电视列车到站时间监控
日常地铁站中会有专门的电视来展示下一班地铁到站的时间表,该系统中也模拟该效果,不过该系统暂时做了电视的模型,时间暂无对接,以下为效果图:
场景监控交互
3D 场景中交互是比较简单的,主要是点击摄像头展示 2D 监控面板,在 2D 界面中主要是切换三种交互模式,三种交互模式为互斥的关系,以下是 3D 交互注册事件代码:
1 g3d.mi((e) = >{
2 let {
3 g2d,
4 dm2d
5 } = this;
6 // 为点击类型
7 if (e.kind === ‘clickData‘) {
8 // data 为当前点击的图元
9 let data = e.data;
10 // 当前图元的 shape3d 类型
11 let shape3d = data.s(‘shape3d‘);
12 // 判断当前 shape3d 类型是否为摄像头
13 if (shape3d && shape3d.indexOf(‘摄像头‘) > 0) {
14 let cameraPanel = dm2d.getDataByTag(‘cameraPanel‘);
15 // toggle 切换摄像头 2d 面板
16 g2d.isVisible(cameraPanel) ? cameraPanel.s(‘2d.visible‘, false) : cameraPanel.s(‘2d.visible‘, true);
17 }
18 }
19 // 为点击 3d 场景背景类型
20 if (e.kind === ‘clickBackground‘) {
21 let cameraPanel = dm2d.getDataByTag(‘cameraPanel‘);
22 // 隐藏摄像头 2d 面板
23 g2d.isVisible(cameraPanel) && cameraPanel.s(‘2d.visible‘, false);
24 }
25 });
总结
工业互联网将人,数据和机器连接起来,地铁站 3D 可视化系统则是一个很好的展现,HT 的轻量化,数据的可视化,机器的可视化,资产的管理化帮助我们更好的监控。而物联网将通过各种信息传感设备,实时采集任何需要监控、连接、互动的物体或过程等各种需要的信息,通过与 HT 的结合更好的展现出可视化的优势,当然地铁站还可以与 VR 进行结合,在各地科技展会中我们可以见到各种 VR 场景操作,HT 中也可以结合 VR 设备进行操作,可以戴上设备在地铁站中漫游,让人有身临其境的感觉,由于场景本身的轻量化,所以 VR 场景下的流畅性也是十分的高,让用户不会有头晕的感觉。当然系统本身也可以在移动端运行,以下为移动端运行截图:
程序运行截图:
应用程序在某一时刻,可能需要在数据库中存储"大"数据。
"大"通常意味着"大约 4kb 或以上",尽管某些数据库在数据达到"大"之前可以轻松地处理多达 32kb 的数据。大对象本质上可能是文本或二进制。
在 PDOStatement::bindParam() 或 PDOStatement::bindColumn()) 调用中使用 PDO::PARAM_LOB 类型码可以让 PDO 使用大数据类型。
PDO::PARAM_LOB 告诉 PDO 作为流来映射数据,以便能使用 PHP Streams API 来操作。
从数据库中显示一张图片
下面例子绑定一个 LOB 到 $lob 变量,然后用 fpassthru() 将其发送到浏览器。因为 LOB 代表一个流,所以类似 fgets()、fread() 以及 stream_get_contents() 这样的函数都可以用在它上面。
<?php
$db = new PDO(‘odbc:SAMPLE‘, ‘db2inst1‘, ‘ibmdb2‘);
$stmt = $db->prepare("select contenttype, imagedata from images where id=?");
$stmt->execute(array($_GET[‘id‘]));
$stmt->bindColumn(1, $type, PDO::PARAM_STR, 256);
$stmt->bindColumn(2, $lob, PDO::PARAM_LOB);
$stmt->fetch(PDO::FETCH_BOUND);
header("Content-Type: $type");
fpassthru($lob);
?>
插入一张图片到数据库
下面例子打开一个文件并将文件句柄传给 PDO 来做为一个 LOB 插入。PDO尽可能地让数据库以最有效的方式获取文件内容。
<?php
$db = new PDO(‘odbc:SAMPLE‘, ‘db2inst1‘, ‘ibmdb2‘);
$stmt = $db->prepare("insert into images (id, contenttype, imagedata) values (?, ?, ?)");
$id = get_new_id(); // 调用某个函数来分配一个新 ID
// 假设处理一个文件上传
// 可以在 PHP 文档中找到更多的信息
$fp = fopen($_FILES[‘file‘][‘tmp_name‘], ‘rb‘);
$stmt->bindParam(1, $id);
$stmt->bindParam(2, $_FILES[‘file‘][‘type‘]);
$stmt->bindParam(3, $fp, PDO::PARAM_LOB);
$db->beginTransaction();
$stmt->execute();
$db->commit();
?>
插入一张图片到数据库:Oracle
对于从文件插入一个 lob,Oracle略有不同。必须在事务之后进行插入,否则当执行查询时导致新近插入 LOB 将以0长度被隐式提交:
<?php
$db = new PDO(‘oci:‘, ‘scott‘, ‘tiger‘);
$stmt = $db->prepare("insert into images (id, contenttype, imagedata) " .
"VALUES (?, ?, EMPTY_BLOB()) RETURNING imagedata INTO ?");
$id = get_new_id(); // 调用某个函数来分配一个新 ID
// 假设处理一个文件上传
// 可以在 PHP 文档中找到更多的信息
$fp = fopen($_FILES[‘file‘][‘tmp_name‘], ‘rb‘);
$stmt->bindParam(1, $id);
$stmt->bindParam(2, $_FILES[‘file‘][‘type‘]);
$stmt->bindParam(3, $fp, PDO::PARAM_LOB);
$stmt->beginTransaction();
$stmt->execute();
$stmt->commit();
?>
将本地已有的一个非git项目上传到新建的git仓库的方法一共有两种。
一、 克隆+拷贝
第一种方法比较简单,直接用把远程仓库拉到本地,然后再把自己本地的项目拷贝到仓库中去。然后push到远程仓库上去即可。此方法适用于本地项目不是一个git仓库的情况。
具体步骤如下:
1、首先克隆
git push -u origin master
git init
git add .
git commit -m "push current files"
git remote add origin git@github.com:yuanmingchen/tensorflow_study.git
git pull origin master --allow-unrelated-histories
git push -u origin master
git remote rm origin
git branch -a
git branch xf
git checkout xf
git pull origin master:xf
git push origin xf:master
1
什么是Serlvet ?
全称 server applet 运行在服务端的小程序:
首先来说,这个servlet是java语言编写的出来的应用程序,换句话说servlet拥有java语言全部的优点,比如跨越平台,一次编译到处运行
其次: 相对于CGI(common gateway interface)规范而言,CGI是针对每一个用户的请求创建一个进程处理,而servlet所在的服务器会对每一个请求创建一个线程来处理,虽然线程数量有上限,但是相对于创建进程来说,后者对系统资源的开销更小
然后就是: 现在盛行javaWeb服务器Tomcat也是java语言编写的,毕竟Tomcat有Serlvet容器支持,所以servlet和web服务器之间无缝连接
Servlet其实一个接口,一套规范,不同的厂家对它有不同的实现,tomcat也是如此,
web服务器会把解析http协议信息的逻辑封装进他们的Servlet中,比如将用户发送的请求(request) ,
把响应给用户http报文的逻辑封装进中, 然后web服务器负责不同组件,不同servlet之间的调度关系,
什么是调度呢? 比如说: 通过某个URL找到指定的Servlet,回调Servlet的方法处理请求
Servlet的体系结构
servlet接口的实现类如上图
Servlet在java中是一个接口,封装了被浏览器访问到服务器(tomcat)的规则
添加serlvet
通过web.xml
通过注解
舍弃web.xml是serlet3.0添加全注解技术, 这个注解的属性和需要在xml中配置的对应的
需要Tomcat7及以上才支持
servlet的路径定义规则
/xxx
/xxx/yyy
/xxx/*
*.do
执行原理:
tomcat读取xml配置文件中配置servlet,根据用户配置的加载时机,通过反射技术创建出对象实例
用户的请求报文经过tomcat的解析,分发到的Servlet下面,进行不同的回调处理
Servlet接口的方法
初始化方法, 创建servlet时执行一次
什么时候被创建: 默认情况下 第一次访问时被创建
一般我们都在web.xml配置,让Servlet在启动时完成加载 默认这个值是-1, 表示第一次访问时被创建, 整数表示启动时初始化
此外: Servlet的init()方法仅仅被执行一次,说明serlet是单例的,那么在并发的情况的就可能出现线程安全问题 , 解决: 尽量不要在serlvet中定义成员变量,我们最好去成员方法中定义变量,即使定义了, 不要提供set()方法,仅仅提供的get()
获取serlvet config 配置对象
提供服务的方法, 每次serlvet被访问都会执行一次
获取serlvet 的信息, 版本等
服务器正常关闭前, 销毁servlet时 回调
服务器非正常关闭,不会执行
Servlet3.0新特性
Servlet3.0中的重大升级是,通过这个技术使我们可以为现有的组件写出可插拔的组件,与之相对应的是Servlet的新规范如下:
在执行的路径下面创建指定的文件
我们可以在上面的文件中配置一个类的全类名,这个类是谁无所谓,但是只要它实现了这个接口,并重写它的方法,于是当容器(tomcat)启动的时候就会调用这个类的 方法
这个规范带来的革命决定是历史性的,有了它我们的代码就有了可插拔的能力,不信可以回想一下传统的配置文件,如果想给项目进行升级,还不想改动xml文件,那是不可能的,但是现在不同了,只要让我们的类实现这个,重写它的方法,它的就会被回调,而其他的功能不受响应,去掉这个类,项目整体也不受响应
示例:
容器启动的时候,会把容器中,被 中指定的所有实现类(子类,子接口)的实例传递进下面的set集合
通过上面方法可以看到,第二个参数位置上是 ServletContext, 这个对象是什么?有啥用? 在下文中单独开一个模块说
ServletContext
tomcat会为每一个web项目创建一个全局唯一的ServeltContext,这个对象里面封装着整个应用的信息,常用的当作域对象,所有的servlet之间共享数据,同时他还可以获取出web.xml文件中的数据
功能:
获取MIME类型
MIME类型是互联网通信中定义的文件数据类型
格式: 大类型/小类型 如: test/html
在tomcat的配置文件目录中存在web.xml ,里面的存在大量的MEMI类型的数据,都可以从t中获取出来
域对象(共享数据)
范围: 类似于Session,通过ServletContext对象我们也可以实现数据共享,但值得注意的是,Session是只能在一个客户端中共享数据,而ServletContext中的数据是在所有客户端中都可以实现数据共享的。
方法:
获取文件真实的文件路径
方法
实现请求转发
获取web应用的初始化参数
我们可以用标签为servlet配置初始化参数,然后使用ServletConfig对象获取这些参数,假如有如下的MyServlet,它的配置为:
获取:
如何获取:
在web应用上下文中以单例的形式存在,下面两种获取方式得到的是同一个对象
生命周期
服务器一启动就创建,服务器关闭时才销毁
注册三大web组件(servlet filter listener)
Servlet
Filter
监听器
Spring-web对Servlet3.0的应用
先上一张继承体系图,下面围绕这张图片展开
可以看到,Spring应用一启动就会加载接口下的所有组件,并且,只要这些组件不是接口,不是抽象类,Spring就为它们创建实例
更进一步看一下上下文中接口的实现类
AbstractContextLoaderInitializer
看他对方法的重写, 主要干了什么呢? 注册了一个上下文的监听器(借助这个监听器读取SpringMvc的配置文件),初始化应用的上下文
AbstractDispatcherServletInitializer
见名知意,他是的初始化器,他主要做了什么事呢?
上面看了,它的父类初始化上下文,于是它调用父类的构造,往上传递web环境的上下文
紧接着添加
AbstractAnnotationConfigDispatcherServletInitializer
重写了父类的创建上下文的方法,我觉得这算是一个高潮吧, 因为啥呢,AnnotationConfigWebApplicationContext是SpringMvc使用的应用的上下文,怎么创建的源码在下面,其实我有在Spring源码阅读中写过这个方面的笔记,下面仅仅是将配置类传递给Spring的bean工厂,并没有对配置类进行其他方面的解析,或者是扫描包啥的
重写了它父类的创建serlvet上下文的方法,
有个点,大家有没有发现,SpringMvc的上下文和Servlet的上下文是同一个对象,都是,不同点就是添加了if-else分支判断,防止重复创建
对比官网推荐的启动案例:
下面的是Spring官网推荐是通过注解的配置方法,仔细看看,其实和上面的Spring-Web模块的做法是一样的
基于Servlet3.0全注解方式整合SpringMvc
经过前面的分析,第一个结论是:服务器一启动,经过自上而下的继承体系会被加载执行,所以,当我们想使用全注解方式完成继承SpringMVC时,继承就好
下面的两个配置类, 按照他的意思,分成了两个配置类,一个是web上下文中的配置类,另一个是Spring原生环境的配置类
但是吧,看看下面的配置真的是特别麻烦,一个得排除,完事另一个得包含,其实Spring原生上下文都认识这些通用注解,倒不如直接就一个配置类,还省事
Servlet3.0 异步请求处理器
早前的前后端请求响应的模型是怎样的呢? 用户发送的请求经过网络传输到Tomcat,Tomcat中存在一个线程池,这时Tomcat会从线程池中取出一条线程专门处理这个请求,一直到处理完毕,给了用户响应之后才将此线程回收到线程池,但是线程池中的线程终究是有限的,一旦同时好几百的连接进来,Tomcat的压力骤然上升,难免会出现阻塞的现象
Serlet3.0引入的异步处理,让主线程拥有非阻塞的特性,这样tomcat接收请求访问的吞吐量就会增加
示例:
SpringMvc的异步任务
针对Servlet3.0的异步特性,SpringMvc相关的支持是提供了异步线程池
DeferredResult
我觉得这个异步的实现方式简直是无与伦比!!!无法言表!!!
Callable
方法的最后将返回
方法中做写需要异步处理器的逻辑
执行流程:
SpringMvc会将这个Callable放到一个叫TaskExcutor中执行
DispatcherSerlvet和所有的Filter退出web容器,但是Response保持打开状态
SpringMvc会将Callable的返回结果重写派发给setlvet恢复之前的处理
根据Callable返回的结果SpringMvc进行渲染
Request
请求方式与很多种,get post head trace options 还有put delete
其中get post put delete 是RestfulAPI中推荐,也是现在盛行使用的四种请求方法
get: 最为简单的请求方式,一般数据添加在url后面一般这样写, 由于URL的长度有限制,故能传输的数据一般在1M左右, 而且数据明文传输,像上面那样,村咋存在安全隐患
post的数据存放在请求体中,一般没有大小限制,相对于get而言,post的安全性更好一点
继承图如下:
上图中我们最常使用的竟然是个接口,当时一开始学web的时候确实觉得很奇怪,但是现在想想其实也还好了,因为Tomcat提供了实现类
获取请求行数据-GET
请求行: GET /test/app?name=zhangsan http/1.1
获取请求方法: GET
获取虚拟路径(项目路径): /test
获取Servlet路径; /app
获取get请求的请求参数: name=zhangsan
获取URI : /test/app
获取URL : http:localhost/test/app
获取协议版本
获取远程主机地址
获取请求头数据
根据名称获取请求头
获取所有的请求头
获取请求体数据
仅仅有post方式,才会有请求体:使用它分成两步:
从request中获取流对象
从流对象中获取到需要的数据
通用的方法
根据参数名获取参数值
根据参数名,获取参数值数组
获取所有请求的参数名称
获取所有参数键值对形式的map集合
有了通用的方法特性之后,我们就不跟针对doGet,doPost两种方式写两份代码, 只要在doGet()或者doPost()中调用另外一个就ok,因为方法针对两者通用
解决中文乱码
首先: Tomcat8自身解决了中文乱码问题
Post方式提交数据依然存在乱码问题,像下面这样先设置编码再使用 req
request的请求转发
当用户的某一个请求需要通过多个Servlet协作完成时,请求在Servlet之间跳转,这种资源跳转的方式称为请求转发
使用方法:通过当前的request获取出RequestDispacher对象,通过这个对象的forward(req,res)进行转发的动作
特点:
浏览器地址栏路径没有发生变化
服务器内部官网的资源跳转,不能跳往别的站点
一次转发,对浏览器来说,仅仅发送了一次请求
因为我们没让浏览器发送两次请求,在服务端完成了请求转发,所以上面的path仅仅是servlet-url-pattern,而不包含项目路径
域对象-共享数据
域对象: request域, 既然是域对象,他就有自己的作用范围,request的作用范围是什么呢? 就是一次请求,每次请求都是一个域, 换句话说,如果说客户端的一次请求经过了AServlet,然后AServlet将请求转发到了BServlet,name AServlet BServlet就在一个域中,也就可以共享彼此的数据
怎么玩?
Reponse
响应信息格式如下:
常用方法
Response对象就是用来设置响应消息的对象
设置响应行
设置响应头
设置响应体
重定向
特点:
重定向: 浏览器地址栏路径改变了
重定向: 可以请求其他服务器
重定向: 实际上发起了两次请求 (不能使用request域共享数据)
因为我们让浏览器发送了两次请求, 因此重定向的路径中包含 项目路径
Cookie
常用api
客户端会话技术,将数据保存在浏览器本地, 下一次访问时会携带着cookie
创建cookie,绑定数据
发送cookie
获取解析cookie
一次发送多个cookie
cookie的生命周期
默认cookie存储在浏览器内存中,一旦浏览器关闭,cookie销毁
设置cookie的生命周期
cookie存储中文
tomcat8之前,cookie不支持中文,Tomcat8之后cookie支持中文
tomcat8之前需要进行转码,一般采用URL编码
cookie获取的范围
默认情况下:在一个tomcat中的部署的多个web项目之间cookie是不能共享的
但是可以通过下面的方法设置更大路径,实现想要的效果
跨域tomcat之间cookie共享使用-- 根据域名划分
特点:
存储在浏览器,不安全
浏览器对单个cookie大小(一般都在4kb),对同一个域名下的cookie总数也有限制(一般20个以内)
小场景:
服务器,浏览器协作的流程: 比如登录: 用户通过浏览器往服务端发送登录请求,服务端验证用户名密码,通过后往客户端的发送cookie, 其实是设置了响应头set-cookie=XXX, 浏览器碰到这种响应头,就把这个响应体缓存在本地,再次请求这个网站时,会自动携带这个请求头cookie=XXX , 后端通过解析用户发送过来的cookie,可以判断当前请求的用户是否是合法的
Session
服务端会话技术,在一次会话中多次请求共享数据,将数据保存在服务端的对象--
session也是域对象
常用API
获取session
使用session
如何确保多次会话中,多次获取到的session是同一个
用户通过浏览器向服务端发送请求
服务端验证用户的信息,通过验证后,如果没有当前的session就为用户创建session(每个session都有唯一的id),创建cookie,给客户端返回相应
用户再次访问服务端,浏览器会自动携带上请求头
服务器解析cookie携带的便可以找出唯一的
细节
客户端关闭,服务端不关闭,两次获取到的session一样吗?
session是依赖cookie的,客户端关闭,cookie被干掉了,也就是说本次会话也就结束了,后端的session将被干掉
但是如果我们发送给浏览器一个可以存活很长时间的cookie,再次打开浏览器访问后端,session还是同一个
客户端不关闭,服务端关闭,两次获取到的session一样吗?
服务器都没了,session肯定被干掉了,session肯定不一样
补救:钝化 tomcat在服务器正常关闭前,将session序列化持久化到磁盘上
补救:活化 tomcat在服务器启动时,将session重新加载进内存
idea中可以成功完成钝化,但是不能完成活化
session失效时间
服务器关闭
使用api,自动关闭
session默认的失效时间30分钟
过期时间可以在Tomcat中的配置文件目录下的web.xml中配置
特点:
session用于存储一次会话的多次请求数据,存储在服务端
session可以存储任意类型的数据没有大小限制
deploy-uat-war-input.war
#!/bin/sh
DOMAIN_HOME=/u01/Oracle..https://www.gxlsystem.com/bin
WLS_LIB=/u01/Oracle/Middleware_https://www.gxlsystem.com/elderberry/server/lib
read -p “Enter Target war file name(s):” app
DEPLOY_SRC=$app
#weblogic config
ADMINURL=t3://192.168.237.66:7001
USERNAME=weblogicuser
PASSWD=weblogicuser123
TARGET=itappau01_7511,itappau02_7511,itappau01_7509,itappau02_7509
for f in $DEPLOY_SRC
do
echo “$f”
name=“${f%.*}”
name=$(echo “$name” | grep -oP -m 1 ‘(.+?(?=-[0-9.]+))|(.+[a-z]’ | head -1)
echo “Try to undeploy — $name”
java -cp .:$CLASSPATH:$WLS_LIB/weblogic.jar weblogic.Deployer -adminurl $ADMINURL -username $USERNAME -password $PASSWD -name $name -undeploy -targets $TARGET
echo “Try to deploy — $name”
java -cp .:$CLASSPATH:$WLS_LIB/weblogic.jar weblogic.Deployer -adminurl $ADMINURL -username $USERNAME -password $PASSWD -name $name -deploy -upload $f -targets $TARGET
done
前置:
1、Http请求是基于Tcp connection这个链接的
2、位码即tcp标志位,有6种标示:
SYN(synchronous建立联机) 、ACK(acknowledgement 确认)、 PSH(push传送)
FIN(finish结束)、RST(reset重置)、 URG(urgent紧急)
Sequence number(顺序号码)
Acknowledge number(确认号码)
SYN (同步序列编号):是 TCP/IP 建立连接时使用的握手信号。TCP 连接的第一个包,非常小的一种数据包。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN + ACK 应答表示接收到了这个消息,最后客户机再以 ACK 消息响应。这样在客户机和服务器之间才能建立起可靠的 TCP 连接,数据才可以在客户机和服务器之间传递。
Seq (序号):是 TCP 可靠传输的关键部分。序号是该报文段发送的数据组的第一个字节的序号。在 TCP 传送的流中,每一个字节都有一个序号。比如一个报文段的序号为 300,报文段数据部分共有 100 字节,则下一个报文段的序号为 400 。所以序号确保了 TCP 传输的有序性。
ACK (确认值):仅当 ACK = 1 时,确认号字段才有效。TCP 规定,在连接建立后所有报文的传输都必须把 ACK 置 1 。
Ack (确认号):指明下一个期待收到的字节序号,表明该序号之前的所有数据已经正确无误的收到。确认号只有当 ACK 标志为 1 时才有效。比如建立连接时,SYN 报文的 ACK 标志位为 0 。
状态:
CLOSED:表示初始状态
SYN_SENT:表示客户端已发送 SYN 报文
SYN_RCVD:表示服务器接受到了 SYN 报文
ESTABLISHED:表示连接已经建立
过程:
第一次握手:建立连接时,客户端发送 SYN 到服务器,并进入 SYN_SENT 状态
第二次握手:服务器收到请求后,回送 SYN + ACK 到客户端,此时服务器进入 SYN_RECV 状态;
第三次握手:客户端收到 SYN + ACK 包,向服务器发送确认 ACK 包,客户端进入 ESTABLISHED 状态,服务器收到请求后也进入 ESTABLISHED 状态,完成三次握手,此时 TCP 连接成功,客户端与服务器开始传送数据
第一次握手 (客户端向服务器发送):Client 首先发送一个连接试探,ACK = 0 表示确认号无效,SYN = 1 表示这是一个连接请求或连接接受报文,同时表示这个数据报不能携带数据,seq = x 表示 Client 自己的初始序号( seq = 0 就代表这是第 0 号帧),这时候 Client 进入 SYN_SENT 状态,表示客户端等待服务器的回复
第二次握手 (服务器回复客户端):Server 监听到连接请求报文后,如同意建立连接,则向 Client 发送确认。
TCP 报文首部中的 SYN 和 ACK 都置为 1 ,ack = x + 1表示期望收到对方下一个报文段的第一个数据字节序号是 x + 1 ,同时表明 x 为止的所有数据都已正确收到( ack = 1 其实是 ack = 0 + 1 ,也就是期望客户端的第 1 个帧),seq = y 表示 Server 自己的初始序号( seq = 0 就代表这是服务器这边发出的第 0 号帧)。这时服务器进入 SYN_RCVD 状态,表示服务器已经收到 Client 的连接请求,等待 Client 的确认
第三次握手 (客户端向服务器发送):Client 收到确认后还需再次发送确认,同时携带要发送给 Server 的数据。ACK 置 1 表示确认号 ack = y + 1 有效(代表期望收到服务器的第 1 个帧),Client 自己的序号 seq = x + 1(表示这就是我的第1个帧,相对于第0个帧来说的),一旦收到 Client 的确认之后,这个 TCP 连接就进入 ESTABLISHED 状态,就可以发起 http 请求了
为什么是三次握手
在第一次通信过程中,A向B发送信息之后,B收到信息后可以确认自己的收信能力和A的发信能力没有问题。
在第二次通信中,B向A发送信息之后,A可以确认自己的发信能力和B的收信能力没有问题,但是B不知道自己的发信能力到底如何,所以就需要第三次通信。
在第三次通信中,A向B发送信息之后,B就可以确认自己的发信能力没有问题。
A-->B-->A-->B这样传三次能保证A有发有收,B也有发有收。多一次浪费少一次不够。
所以为什么是三次握手:最主要是防止已过期的连接再次传到被连接的主机
那为什么非要三次呢?怎么觉得两次就可以完成了。那 TCP 为什么非要进行三次连接呢?在谢希仁的《计算机网络》中是这样说的:
为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
在书中同时举了一个例子,如下:
"已失效的连接请求报文段”的产生在这样一种情况下:
Client 发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达 Server 。本来这是一个早已失效的报文段。但 Server 收到此失效的连接请求报文段后,就误认为是 Client 再次发出的一个新的连接请求。于是就向 Client 发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要 Server 发出确认,新的连接就建立了。由于现在 Client 并没有发出建立连接的请求,因此不会理睬 Server 的确认,也不会向 Server 发送数据。但 Server 却以为新的运输连接已经建立,并一直等待 Client 发来数据。这样,Server 的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,Client 不会向 Server 的确认发出确认。 Server 由于收不到确认,就知道 Client 并没有要求建立连接。"这就很明白了,防止了服务器端的一直等待而浪费资源。
示例分析:
192.168.1.11 为客户端 A,42.121.252.58 为服务器 B。
客户端请求连接发送 SYN 产生一个为 x = 0 的序号 seq 。
服务器接受连接,ACK = x + 1 = 1 并产生 y = 0 的序号 seq;发送 SYN ACK seq = 0 ack = 1 到客户端。
客户端发现服务器接受了连接请求返回了 ack,seq = x;同时接受服务器的请求 ack = y + 1 = 1。
因为上个传输,只传输了一个 ACK 没有数据,所以看第四条线 seq = 1,ack = 1。
收获:看 2 这个项,发送端的 seq 字段需要接收到服务器段的 ack 才会变化,这个时候 服务器 ack = 客户端 seq 。
所以我才会有下面对 seq 和 ack 的理解。
序号 seq :发送了多少被成功接受数据。
确认号 ack:接受了多少数据。
CssStats 是一个在线的 CSS 代码分析工具
网址是: http://www.cssstats.com/
如果你想要更全面的,这个神奇,你值得拥有:
W3C 统一验证工具: ☆☆☆☆☆
因为它可以检测本地文件哦!!
123456789101112131415161718192021222324252627
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> ... </properties><plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>${jdk.version}</source> <target>${jdk.version}</target> <showWarnings>true</showWarnings> <encoding>UTF-8</encoding> </configuration></plugin><!-- resource插件 --><plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.6</version> <configuration> <encoding>UTF-8</encoding> </configuration></plugin>
PDO::ERRMODE_SILENT
此为默认模式。 PDO 将只简单地设置错误码,可使用 PDO::errorCode() 和 PDO::errorInfo() 方法来检查语句和数据库对象。如果错误是由于对语句对象的调用而产生的,那么可以调用那个对象的 PDOStatement::errorCode() 或 PDOStatement::errorInfo() 方法。如果错误是由于调用数据库对象而产生的,那么可以在数据库对象上调用上述两个方法。
PDO::ERRMODE_WARNING
除设置错误码之外,PDO 还将发出一条传统的 E_WARNING 信息。如果只是想看看发生了什么问题且不中断应用程序的流程,那么此设置在调试/测试期间非常有用。
PDO::ERRMODE_EXCEPTION
除设置错误码之外,PDO 还将抛出一个 PDOException 异常类并设置它的属性来反射错误码和错误信息。此设置在调试期间也非常有用,因为它会有效地放大脚本中产生错误的点,从而可以非常快速地指出代码中有问题的潜在区域(记住:如果异常导致脚本终止,则事务被自动回滚)。
异常模式另一个非常有用的是,相比传统 PHP 风格的警告,可以更清晰地构建自己的错误处理,而且比起静默模式和显式地检查每种数据库调用的返回值,异常模式需要的代码/嵌套更少。
创建 PDO 实例并设置错误模式
<?php
$dsn = ‘mysql:dbname=testdb;host=127.0.0.1‘;
$user = ‘dbuser‘;
$password = ‘dbpass‘;
try {
$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo ‘Connection failed: ‘ . $e->getMessage();
}
?>
注意: 不管当前是否设置了 PDO::ATTR_ERRMODE ,如果连接失败,PDO::__construct() 将总是抛出一个 PDOException 异常。未捕获异常是致命的。
创建 PDO 实例并在构造函数中设置错误模式
<?php
$dsn = ‘mysql:dbname=test;host=127.0.0.1‘;
$user = ‘googleguy‘;
$password = ‘googleguy‘;
/*
使用 try/catch 围绕构造函数仍然有效,即使设置了 ERRMODE 为 WARNING,
因为如果连接失败,PDO::__construct 将总是抛出一个 PDOException 异常。
*/
try {
$dbh = new PDO($dsn, $user, $password, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));
} catch (PDOException $e) {
echo ‘Connection failed: ‘ . $e->getMessage();
exit;
}
// 这里将导致 PDO 抛出一个 E_WARNING 级别的错误,而不是 一个异常 (当数据表不存在时)
$dbh->query("SELECT wrongcolumn FROM wrongtable");
?>
以上例程会输出:
Warning: PDO::query(): SQLSTATE[42S02]: Base table or view not found: 1146 Table ‘test.wrongtable‘ doesn‘t exist in
/tmp/pdo_test.php on line 18
add a note add a note
LAMP平台概述
构成组件
LAMP的优势
一,在Windows上将LAMP所需压缩软件包共享出来(此处如有问题请看之前的博客相关文章)
二,在Linux上使用远程共享获取文件并挂载到mnt目录下
三,编译安装Apache
1,将源码包解压到/opt目录下
2,将apr组件包移动到http中并安装编译工具
3,配置安装目录及各种模块
4,编译和安装
5,替换启动脚本并配置http文件参数
6,创建软连接,方便管理
7,关闭防火墙并开启服务
四,编译安装MySQL
1,安装环境包并解压软件包到/opt下
2,配置MySQL的安装目录及其他模块配置
3,编辑及安装
4,配置MySQL数据库
5,开启数据库并查看端口号,设置数据库用户密码
五,安装PHP
1,安装php环境并解压源码包到/opt下
2,配置PHP相关参数及关联数据库和http服务
3,编辑及安装
4,创建软连接并配置http配置文件
5,编辑PHP首页信息,并重启http服务
6,验证PHP测试网页
六,安装Discuz论坛软件
1,解压缩压缩包到/opt下,并将目录内容复制到http站点中
2,进入数据库创建bbs数据库并设置管理员和密码
3,进入站点并给程序用户提权
4,访问192.168.235.137/bbs站点,安装Discuz论坛
5,设置运行环境为全新安装
6,安装数据库
数据服务器:192.168.235.137(此处输入创建数据库主机的IP)数据库名:bbs数据库用户名:bbsuser(用户名可在命令行修改)数据库密码:admin123(密码可在命令行修改)管理员账号:admin(该账号为默认)密码:123123(密码可直接在网页设定)
七,安装完成,进入论坛首页,一个完整的结构搭建完成了
谢谢阅读!!
通过W3C 统一验证工具的检测没有错误后,为了提高加载速度和节约空间(相对来说,css量很少的情况下,几乎没啥区别),可以通过css压缩工具把css进行压缩。
w3c css压缩 网速比较慢
还可以去站长之家进行快速压缩。
如何进行WordPress网站建设一直是困扰许多建站人员的一个大问题,不是他们不会建一个网站,而是如何判断网站建设的优异程度,行业标准和搜索引擎标准将影响网站建设的最终效果。无论是传统的建站方式,还是自助建站,还是定制的发明,团队都应该认识到站点的定位。为了从人群中脱颖而出,我们必须对网站建设有一个清晰的认识。那么WordPress网站建设过程中需要避免哪些问题?
这里是为了解决WordPress网站建设中需要避免的几个问题。许多企业只显示单位介绍、活动等信息,充其量只能发挥显示板的作用,根本不利用网络的交互作用。实际上,现代网站建设,是为了使企业和用户能够立即联系和交流,总之,是一种渠道和平台,应该提高其利用率。
网站不应该添加太多的图片、动态效果和音乐等元素,这必然会影响网站的开放速度和流畅性,一些不耐烦的用户看到网站被直接关闭。”WordPress企业网站的建设应保持简洁、清晰,以免受到用户的喜爱,不受不必要的视觉信息的干扰。在这个信息时代,我们都希望第一次看到最新鲜的内容,没有人对过期的信息感兴趣,所以必须一直迭代内容。有些企业网站如果几个月或六个月没有更新,就会感到毫无生气。每天维护和上传有关企业的信息是非常重要的。这是非常重要的,让人们感觉到网站正在被照顾,就像一个商店。进去是尘埃落定的事,它会让人望而却步,一定要让网站活下去。
新网站,试着在主页上对网站的性质和内容进行简要的描述和介绍,让其他人判断是否继续点击。尝试在首页上有非常清晰的列选项,最好是人性化,这样读者就有机会快速获得他们所需要的信息。在规划中,要坚持清洁和新鲜度的原则。如果首页没有特殊的需要,最好不要放大照片,或者添加一点完美的上班族,因为这样会增加下载的精力和时间,使读者失去耐心;第二,画面不应该杂乱无章,让用户找不到他们想要的信息。
这是我们在建设网站时需要注意的几点,虽然还有很多话要谈,但基本上要注意以上几点,WordPress网站建设需要人们慢慢学习和积累,这是非常重要的。
curl: (23) Failed writing body (7818 != 16384)
loop: module loaded
dracut-initqueue[579]: mount: wrong fs type, bad option, bad superblock on /dev/loop0
dracut-initqueue[579]: missing codepage or helper program, or other error
dracut-initqueue[579]: In some cases useful info is found in syslog - try
dracut-initqueue[579]: dmesg I tail or so.
dracut-initqueue[579]:Mumount: /run/initramfs/squashfs: not mounted
dracut-initqueue[579]: /sbin/dmsquash-lice-root: line 286: printf: write error: No space left on device
这个提示是因为分配给guest内存不足导致,提高内存即可解决。(这个内存大小自己可以尝试,我加到2G就解决了。)
Mega menu(大幅网页菜单)是在网页界面设计中非常常见的元素之一,用于引导用户进入下级页面。但是,正因为mega menu十分常见,所以如果总是一成不变的设计,就容易显得乏味。
本文收集了一些优秀的大牌网站mega menu设计案例,有的设计细节到位美观,有的动效细腻流畅,相当值得参考!下面一起来欣赏吧,Enjoy!
Moosejaw
Moosejaw.com是一家在线实体零售商,专门从事户外休闲服装和滑雪板、攀岩、远足和露营装备。该网站的mega menu里尽可能全面地罗列了网站的各分类,二级分类下分别又以首字母、性别、年龄、商品登进行划分,十分地详细,是非常经典的电商菜单。
Estee Lauder
雅诗兰黛是一家非常出名的化妆品品牌,旗下的各种化妆品和护肤品非常受欢迎。整个网站采用了黑白主色+大图的设计风格。mega menu设计也十分地简洁,统一采用黑色字体,仅通过加粗、字母大写来强调层级。
Envelopes
Envelopes是一家销售各种信封和邮寄材料的网站。网站的mega menu中,产品根据颜色、尺寸、样式和纸张纹理排序,划分详细,提高搜索效率,帮助用户快速找到最佳解决方案。每种颜色前面还有色卡,非常贴心了。
Quiksilver
Quiksilver的(Boardriders公司)是澳大利亚零售运动品牌,是世界上最大的冲浪服和其他与运动相关的设备品牌之一。其网站采用了黑白主色+大图的设计风格。mega menu同样配合这样的极简风格,整体居中,留有大面积空白。统一采用黑色字体,仅通过加粗、字母大写来强调层级。采用Hover到文字上时置灰的交互方式,显得非常灵敏。
FAO Schwarz
FAO Schwarz是一家美国玩具品牌,以其高端玩具、真人大小的毛绒动物、互动体验、品牌整合和游戏而闻名。网站的mega menu采用了上下渐入、先左后右的动效,显得动态十足,非常有趣。
Bras N Things
Bras N Things是一家卖女性内衣的电商网站。网站的mega menu动效细腻流畅,二级菜单采用了纯文字和文字搭配小图这两种设计布局。
The Sak
The Sak是一家卖包的电商网站。网站的mega menu总体采用了上下谈出、交替出现的动效,鼠标Hover选项时出现伸缩的下划线,交互效果流畅。
Piano
Piano是一家专门帮助媒体公司和内容运营公司成长的团队。网站首屏有一个动效炫酷的转动的地球,除此之外网站的设计较为扁平,其mega menu采用最简单隐藏交互动效,且仅有两个层级。
Evernote
Evernote是一款出名的电子笔记资料管理软件。网站整体采用了绿色,色调十分统一。mega menu中,鼠标Hover至二级菜单选项时,整个选项板块即会变成绿色。
Beyond Trust
BeyondTrust是一家为UNIX/Linux/Windows/Mac OS操作系统提供权限管理的美国公司。网站mega menu二级菜单间采用横向排列的布局,二级菜单下的选项采用纵向排列的布局,层级清晰,一目了然。鼠标Hover至选项时,文字变成系统红色。
Mega menu原型设计:
让我们看看在Mockplus中的mega menu设计:
————————————————
版权声明:本文为CSDN博主「Mockplus」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jongde1/article/details/100770345
/**
* 算术运算符:+, -, *, /, %
* 当对非number类型的值进行运算(-, *, /, %)时,会将这些值先转换成number再运算,加法‘+‘运算除外,
* 当对非number类型(string, object)的值做加法(+)运算时会将这些值先转换成string再做拼接而不是相加
* number类型的值与NaN做运算都是NaN
*/
// number
console.log(`1 + 1 = ${1 + 1}`); // 1 + 1 = 2
console.log(`1 + NaN = ${1 + NaN}`); // 1 + NaN = NaN
// string
console.log(`1 + ‘foo‘ = ${1 + ‘foo‘}`); // 1 + ‘foo‘ = 1foo
// boolean
console.log(`1 + true = ${1 + true}`); // 1 + true = 2
console.log(`1 + false = ${1 + false}`); // 1 + false = 1
// null
console.log(`1 + null = ${1 + null}`); // 1 + null = 1
// undefined
console.log(`1 + undefined = ${1 + undefined}`); // 1 + undefined = NaN
// object
console.log(`1 + (new Object()) = ${1 + (new Object())}, typeof (new Object()) = ${typeof (new Object())}`); // 1 + (new Object()) = 1[object Object], typeof (new Object()) = object
/**
* 一元运算符:-, +
* 对于number类型数字,+不会对数字产生影响,-将数字转换成负数
* 对于非number类型的值,会先将值转换成number再做运算
*/
// number
console.log(`+1 = ${+1}`); // +1 = 1
console.log(`-1 = ${-1}`); // -1 = -1
console.log(`+NaN = ${+NaN}`); // +NaN = NaN
// string
console.log(`+‘a‘ = ${+‘a‘}`); // +‘a‘ = NaN
console.log(`+‘‘ = ${+‘‘}`); // +‘‘ = 0
console.log(`+‘1‘ = ${+‘1‘}`); // +‘1‘ = 1
// boolean
console.log(`-true = ${-true}`); // -true = -1
console.log(`-false = ${-false}`); // -false = 0
// null
console.log(`-null = ${-null}`); // -null = 0
// undefined
console.log(`-undefined = ${-undefined}`); // -undefined = NaN
// object
console.log(`+(new Object()) = ${+(new Object())}`); // +(new Object()) = NaN
// 注意:以上显示结果仅是在谷歌浏览器下(正式版本71.0.3578.98 (64位))
function GetRequest() {
var url = location.search; //获取url中"?"符后的字串
var theRequest = new Object();
if (url.indexOf("?") != -1) {
var str = url.substr(1);
strs = str.split("&");
for(var i = 0; i < strs.length; i ++) {
theRequest[strs[i].split("=")[0]] = decodeURI(strs[i].split("=")[1]);
}
}
return theRequest;
}
使用:
var Request = new Object();
Request = GetRequest();
var 变量名 = Request[‘要提取的URL中的变量名‘];
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
body{position:relative;}
*{padding:0;margin:0;}
.dialog{width:400px;height:400px;border:1px solid #ddd;position:fixed;top:100px;left:200px;}
.dialog h2{height:40px;background:purple;cursor:move;}
.mask{position:fixed;width:0;height:0;left:0;top:0;display:none;border: 3px solid #eee;box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.07);cursor:move;}
</style>
</head>
<body>
<div class="dialog" id="box">
<h2 id=‘digTit‘></h2>
</div>
<div class="mask"></div>
<script type="text/javascript">
var dragEle = document.getElementById(‘digTit‘)
var box = document.getElementById(‘box‘)
var x,y // 物体离上边,下边的距离
var moveBox = document.querySelector(‘.mask‘) // 移动的是这层
// 设置居中
var oldX = (document.documentElement.clientWidth-box.offsetWidth)/2
var oldY = (document.documentElement.clientHeight-box.offsetHeight)/2
box.style.left = oldX + ‘px‘
box.style.top = oldY + ‘px‘
moveBox.style.left = oldX + ‘px‘
moveBox.style.top = oldY + ‘px‘
moveBox.style.width = box.offsetWidth + ‘px‘
moveBox.style.height = box.offsetHeight + ‘px‘
digTit.addEventListener(‘mousedown‘, function (e) {
moveBox.style.display = ‘block‘
var beginX = e.clientX
var beginY = e.clientY
document.addEventListener(‘mousemove‘, move, false)
document.addEventListener(‘mouseup‘, up, false)
function move(e) {
x = oldX + e.clientX - beginX
y = oldY + e.clientY - beginY
moveBox.style.left = x + ‘px‘
moveBox.style.top = y + ‘px‘
}
function up(e) {
box.style.left = x + ‘px‘
box.style.top = y + ‘px‘
oldX = x
oldY = y
moveBox.style.display = ‘none‘
document.removeEventListener(‘mousemove‘, move, false)
document.removeEventListener(‘mouseup‘, up, false)
}
}, false)
</script>
</body>
</html>
HTTP协议(超文本协议)
四大特性
基于TCP/IP之上作用于应用层
基于请求响应
发是请求,给是响应
无状态
不保存用户状态,连一次就给忘了
无连接
eg:one night love
数据格式
请求格式
请求首行(请求方式,协议版本等)
请求头(一大堆K:V键值对)
请求头(真正的数据 发post请求的时候才有 如果get请求不会有)
响应格式
响应首行
响应头
响应体
响应状态码
用特定的数字表示一些数据
? 1xx:服务端已经接收到了你的数据 正在处理 你可以提交其他数据
? 2xx:服务端成功响应(200请求成功)
? 3xx:重定向
? 4xx:请求错误(404 请求资源不存在 403 拒绝访问)
? 5xx:(服务器内部错误(500))
请求方式
get请求
? 朝别人要数据
post请求
? 向别人提交数据(eg:用户登录)
纯手撸web框架
手动书写socket
手动处理http格式数据
简单c/s连接
稍微复杂web框架
客户端请求什么,就返回什么,eg:客户端发送则服务器返回给客户端
基于wsgiref
如果我不想写上面代码中的socket连接和里面的index等内容,客户端发送100个不同的请求,我也不能手撸100遍啊!那也太累了吧,所以我们就利用了wsgiref模块,帮我们操作起来更简单和方便,我们可以把在wsgiref.py中的必要代码写在相对应的文件内,以下是各个文件中要放的东西。
? urls.py 路由与视图函数对象关系
? views.py 放的是视图函数(处理业务逻辑的)
? templates 模板文件夹(一堆html文件)
wsgiref.py文件
urls.py
views.py
客户端通过访问服务器获取字典
我们这里需要用到一个模块
我们需要先去下载这个模块
具体了解请点击链接
wsgiref.py文件
urls.py
views.py
get_user.html
动静态页面
静态页面:就是一个写死的页面
动态页面:可以通过更改后端的数据,来反应到浏览器的页面上
? eg:1.后端获取当前时间展示到前端
? 2.后端获取数据库中的数据展示到前端
客户端通过访问服务器获取当前时间
那么如果用户访问服务端,则客户端页面就会显示当前时间,这样该怎么做呢?
我们要知道,页面就是要用到html,如果时间写在HTML,那么通过编码格式,他已经不再是时间了,那么怎样将时间显示在页面呢?
做法:我们要在wsgiref模块的基础上,先在html中写一串特定的字符,在后端将这串html和获取的时间作交换,具体看代码
wsgiref.py文件
urls.py
views.py
get_time.html
客户端通过访问服务器获取数据库数据
获取数据库数据现在就和获取字典的方法差不多像了,只不过我们需要建一个数据库,通过链接数据库,去数据库里面拿值
wsgiref.py文件
urls.py
views.py
get_db.html
我们这里用到了bootstrap搭建页面,这就是一个典型的动态页面
python三大主流web框架
? A:socket部分
? B:路由与视图函数对应关系
? C:模板语法
DjangoFlaskTornado
大而全 自带的功能特别多 类似于航空母舰
有时候过于笨重
小而精 自带的功能特别少 类似于游骑兵
第三方的模块特别多,如果将flask第三方模块全部加起来 完全可以超过django
比较依赖于第三方模块
异步非阻塞
牛逼到可以开发游戏服务器
A用的别人的 wsgiref
B自己写的
C自己写的
A用的别人的 werkzeug(基于wsgiref)
B自己写的
C用的别人的 jinja2
三者全是自己写的
一、安装
cnpm install vue-router -S
如果在一个模块化工程中使用它,必须通过Vue.use()明确安装路由功能:
import Vue from ‘vue‘
import VueRouter from ‘vue-router‘
Vue.use(VueRouter)
二、步骤
1、导入VueRouter包
import VueRouter from ‘vue-router‘
2、手动安装VUeRouter
Vue.use(VueRouter)
3、导入组件
import account from ‘https://www.gxlsystem.com/main/Account.vue‘
import goodsList from ‘https://www.gxlsystem.com/main/GoodsList.vue‘
import login from ‘https://www.gxlsystem.com/subcom/login.vue‘
import register from ‘https://www.gxlsystem.com/subcom/register.vue‘
4、创建路由对象
var router = new VueRouter({
routes = [
{ path: ‘/account‘, component:account}
]
})
5、子路由
routes:[
// account goodlist
{
path:‘/account‘,
component:account,
children:[
{
// 子路由path不带 /
path:‘login‘,
component:login
},
{
path:‘register‘,
component:register
}
]
},
{
path:‘/goodsList‘,
component:goodsList
}
6、将路由挂载到vm上
var vm = new Vue({
el:‘#app‘,
render: c => c(app), // render 会把 el 指定的容器中,所有的内容都清空
// 所以不要把路由的router-view 和router-link直接写到el所控制的元素中
router // 4、将路由对象挂载都vm上
})
// 注意:App这个组件,是通过 vm 实例的render函数渲染出来的,render 函数如果要渲染组件,渲染
// 出来的组件只能放到el:‘#app‘做指定的元素中去
// account 和 goodslist 组件是通过路由匹配的监听的,所以这两个组件只能展示到属于路由的<router-view></router-view>中去
我是先对docker初步到进阶的了解学习,常用的命令,基本的容器构建和docker原理概念学习完之后,步入k8s。即使这样,刚看k8s官方文档时,其设计理念、组件构成以及生态需要了解掌握的知识不在少数。为此,花了几天时间在官方文档&个人博客上做了上述方面初步了解,较为直观的便可分为两方面:
master:
API server
Controller Manager
Scheduler
Etcd
node:
kublet
kube-proxy
pod
Kubernetes集群组件:
etcd 一个高可用的K/V键值对存储和服务发现系统
flannel 实现夸主机的容器网络的通信
kube-apiserver 提供kubernetes集群的API调用
kube-controller-manager 确保集群服务
kube-scheduler 调度容器,分配到Node
kubelet 在Node节点上按照配置文件中定义的容器规格启动容器
kube-proxy 提供网络代理服务
这些是在安装过程中直接接触的组件,了解其用途和原理会更胸有成竹吧。
若没有docker基础,应优先学习docker,在构建k8s时更易切解决碰到的问题。
搭建过程
官网给出的搭建流程不是很满意,会出问题,环境不同异常不同,排查起来需要用时间填坑;此外也有大量开发者给出的个人经验很有借鉴意义但也是有漏洞,或不详尽,或漏洞百出,需要读多篇这样的文章,相互比较找出漏洞形成完整的搭建思路才方便一部搭建到位,说简单也简单,复杂也是。
环境:centos 7
ecs-ali2 master
ecs-ali3 node
因另外一台服务器负载较高,所以就搭建2个节点的集群,还是有别于但节点服务器的。3+节点的单间过程和此文相同,复制node搭建过程便可。
注意:如果你的服务器已经安装过docker,依据我的建议是应该卸载干净,不然会因kubernetes-node和docker产生冲突,导致node启动不了,找不到服务。
master环境搭建
关闭防火墙
我因使用的是iptables,在以前安装centos7时就变更了防火墙软件,不然建议先禁用centos7自带的Firewall
确保epel源
master安装etcd kubernetes-master
编辑/etc/etcd/etcd.conf文件
编辑/etc/kubernetes/apiserver文件
启动etcd、kube-apiserver、kube-controller-manager、kube-scheduler等服务,并设置开机启动。
etcd、kube-apiserver、kube-controller-manager、kube-scheduler等服务停止脚本
执行后,service的status将会是active (running),否则将是空的,或者执行:
在etcd中定义flannel网络
然后你可以访问master节点8080端口,将会返会json格式的节点环境数据!
node环境搭建
执行下面命令前,确认是否已经安装docker,若是则卸载,不然出问题,即使卸载后,kubernetes-node也会再次自动装上docker。
安装flannel和kubernetes-node
编辑/etc/etcd/etcd.conf文件
修改/etc/kubernetes/config文件
在所有Node节点上启动kube-proxy,kubelet,docker,flanneld等服务,并设置开机启动
结束kube-proxy,kubelet,docker,flanneld等服务
执行后,service的status将会是active (running),否则将是空的,或者执行:
集群环境验证
在master上执行如下命令
官方文档采坑点
https://www.kubernetes.org.cn/doc-16,若是参考这个,以下是一些碰到的问题:
在添加添加virt7-testing源,其使用下面信息添加源:
然后安转过程提示:
解决办法:
替换baseurl
反正照着官方汉化文档我没有成功,应该不是我智商着急的原因!
参考文档
<div id="test"></div>
JS:
<script>
function ShowDivBoxOrNot(objName, bShow) {
if (bShow == true) {
document.getElementById(test).style.display = "block";
} else {
document.getElementById(test).style.display = "none";
}
};
</script>
js直接封装了Div的显示与隐藏,一般用于很多弹框!
程序思路,先在JSP上画好页面,然后再创建一Servlet文件用于判断在网页上操作是否正确,还需要与数据库相连接,用DBUtile文件连接数据库,用Dao层来实现数据的增加,用Service来服务于Dao层
其代码如下:
jsp页面代码:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page language="java" import="java.util.*"%>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>注册</title>
</head>
<%
Object message=request.getAttribute("message");
if(!(message==null||message.equals("")))
{
%>
<script type="text/javascript">alert(‘<%=message%>‘)</script>
<%
}
%>
<body align="center">
<form action="LoginServlet?method=register" method="post">
<p name="username" ></p>
<p name="password"></p>
<p name="name" ></p>
<p name="num"></p>
<p name="email"></p>
<p name="xueyuan"></p>
<p name="xi"></p>
<p name="banji"></p>
<p name="dizhi">
<p name="beizhu">
<p value="注册"></p>
</form>
</body>
</html>
Servletceng代码:
package zzz;
import java.io.IOException;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class LoginServlet
*/
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
public static boolean isMatchString(String str,String pattern)
{
boolean isMatch = Pattern.matches(pattern, str);
return isMatch;
}
private static final long serialVersionUID = 1L;
zhuceService fuwu=new zhuceService();
/**
* @see HttpServlet#HttpServlet()
*/
public LoginServlet() {
super();
// TODO Auto-generated constructor stub
}
@Override
protected void service(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException {
ZhuceDao haha=new ZhuceDao();
// TODO 自动生成的方法存根
arg0.setCharacterEncoding("utf-8");
arg1.setCharacterEncoding("utf-8");
//super.service(arg0, arg1);
zhucexinxi sta=new zhucexinxi();
String function=arg0.getParameter("method");
if(function.equals("register"))
{
String str;
str=arg0.getParameter("username");
if(str.length()==0||str==null||str.equals(""))
{
arg0.setAttribute("message","账号不能为空");
arg0.getRequestDispatcher("Login.jsp").forward(arg0,arg1);
return;
}
if(str.charAt(0)>=‘a‘&&str.charAt(0)<=‘z‘||str.charAt(0)>=‘A‘&&str.charAt(0)<=‘Z‘);
else
{
arg0.setAttribute("message","账号第一位需要是字母");
arg0.getRequestDispatcher("Login.jsp").forward(arg0,arg1);
return;
}
if(str.length()<6||str.length()>12)
{
arg0.setAttribute("message", "账号需是6-12位");
arg0.getRequestDispatcher("Login.jsp").forward(arg0, arg1);
return;
}
for(int a=0;a<str.length();a++)
{
if(!(str.charAt(a)>=‘0‘&&str.charAt(a)<=‘9‘||str.charAt(a)>=‘a‘&&str.charAt(a)<=‘z‘||str.charAt(a)>=‘A‘&&str.charAt(a)<=‘Z‘||str.charAt(a)==‘_‘))
{
arg0.setAttribute("message", "账号只能由字母数字下划线组成");
arg0.getRequestDispatcher("Login.jsp").forward(arg0, arg1);
return;
}
}
sta.setYonghuming(str);
str=arg0.getParameter("password");
if(str.length()<8||str==null||str.equals(""))
{
arg0.setAttribute("message", "用户密码需要是8位以上数组字母组成");
arg0.getRequestDispatcher("Login.jsp").forward(arg0,arg1);
return;
}
int flag1=0;
int flag2=0;
for(int b=0;b<str.length();b++)
{
if(str.charAt(b)>=‘0‘&&str.charAt(b)<=‘9‘)
{
flag1=1;
}
if(str.charAt(b)>=‘a‘&&str.charAt(b)<=‘z‘||str.charAt(b)>=‘A‘&&str.charAt(b)<=‘Z‘)
{
flag2=1;
}
}
if(flag1!=1||flag2!=1)
{
arg0.setAttribute("message", "安全程度低,需要由8位以上的数字和字母组成");
arg0.getRequestDispatcher("Login.jsp").forward(arg0, arg1);
return;
}
sta.setPassword(str);
str=arg0.getParameter("sex");
sta.setSex(str);
str=arg0.getParameter("name");
if(str.length()==0||str==null)
{
arg0.setAttribute("message", "姓名不能为空");
arg0.getRequestDispatcher("Login.jsp").forward(arg0, arg1);
return;
}
sta.setMingzi(str);
str=arg0.getParameter("num");
if(str.length()!=8)
{
arg0.setAttribute("message", "学号是由8位数字组成");
arg0.getRequestDispatcher("Login.jsp").forward(arg0, arg1);
return;
}
int flag3=1;
if(str.charAt(0)!=‘2‘)flag3=0;
if(str.charAt(1)!=‘0‘)flag3=0;
if(str.charAt(2)!=‘1‘)flag3=0;
if(str.charAt(3)!=‘8‘)flag3=0;
if(flag3==0)
{
arg0.setAttribute("message", "学号输入错误");
arg0.getRequestDispatcher("Login.jsp").forward(arg0, arg1);
return;
}
sta.setNum(str);
str=arg0.getParameter("email");
boolean nani=LoginServlet.isMatchString(str,"([a-zA-Z0-9])+@([a-zA-Z0-9])+\.([a-zA-Z0-9])+");
if(!nani)
{
arg0.setAttribute("message", "请输入正确格式的邮箱号码");
arg0.getRequestDispatcher("Login.jsp").forward(arg0, arg1);
return;
}
sta.setEmail(str);
str=arg0.getParameter("xueyuan");
sta.setXueyuan(str);
str=arg0.getParameter("xi");
sta.setXi(str);
str=arg0.getParameter("banji");
sta.setBanji(str);
str=arg0.getParameter("ruxue");
sta.setRuxue(str);
str=arg0.getParameter("dizhi");
sta.setDizhi(str);
str=arg0.getParameter("beizhu");
sta.setBeizhu(str);
//判断是否添加数据库成功
if(fuwu.add("tianjiaxuesheng", sta))
{
arg0.setAttribute("message", "注册成功");
arg0.getRequestDispatcher("Login.jsp").forward(arg0, arg1);
}
else
{
arg0.setAttribute("message", "注册失败");
arg0.getRequestDispatcher("Login.jsp").forward(arg0, arg1);
}
}
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
/*protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.getWriter().append("Served at: ").append(request.getContextPath());
}*/
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
/*protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}*/
}
暂存信息层:
package zzz;
public class zhucexinxi {
String yonghuming;
String password;
String sex;
String mingzi;
String num;
String email;
String xueyuan;
String xi;
String banji;
String ruxue;
String dizhi;
String beizhu;
public String getXueyuan() {
return xueyuan;
}
public void setXueyuan(String xueyuan) {
this.xueyuan = xueyuan;
}
public String getYonghuming() {
return yonghuming;
}
public void setYonghuming(String yonghuming) {
this.yonghuming = yonghuming;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getMingzi() {
return mingzi;
}
public void setMingzi(String mingzi) {
this.mingzi = mingzi;
}
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getXi() {
return xi;
}
public void setXi(String xi) {
this.xi = xi;
}
public String getBanji() {
return banji;
}
public void setBanji(String banji) {
this.banji = banji;
}
public String getRuxue() {
return ruxue;
}
public void setRuxue(String ruxue) {
this.ruxue = ruxue;
}
public String getDizhi() {
return dizhi;
}
public void setDizhi(String dizhi) {
this.dizhi = dizhi;
}
public String getBeizhu() {
return beizhu;
}
public void setBeizhu(String beizhu) {
this.beizhu = beizhu;
}
}
数据连接层:
package zzz;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 数据库连接工具
* @author Hu
*
*/
public class DBUtil {
public static String db_url = "jdbc:mysql://localhost:3306/yonghucaozuo?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8";
public static String db_user = "root";
public static String db_pass = "password";
public static Connection getConn () {
Connection conn = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");//加载驱动
conn = DriverManager.getConnection(db_url, db_user, db_pass);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
/**
* 关闭连接
* @param state
* @param conn
*/
public static void close (Statement state, Connection conn) {
if (state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close (ResultSet rs, Statement state, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws SQLException {
Connection conn = getConn();
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql ="select * from course";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
if(rs.next()){
System.out.println("空");
}else{
System.out.println("不空");
}
}
}
Dao层:
package zzz;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class ZhuceDao {
public boolean add(String table,zhucexinxi en)
{
boolean c=false;
String sql="insert into "+table+"(username,password,sex,name,num,email,xueyuan,xi,banji,ruxue,dizhi,beizhu) values(‘"+en.getYonghuming()+"‘,‘"+en.getPassword()+"‘,‘"+en.getSex()+"‘,‘"+en.getMingzi()+"‘,‘"+en.getNum()+"‘,‘"+en.getEmail()+"‘,‘"+en.getXueyuan()+"‘,‘"+en.getXi()+"‘,‘"+en.getBanji()+"‘,‘"+en.getRuxue()+"‘,‘"+en.getDizhi()+"‘,‘"+en.getBeizhu()+"‘);";
Connection conn=DBUtil.getConn();
Statement state=null;
try {
state=conn.createStatement();
int num=state.executeUpdate(sql);
if(num!=0)
c=true;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return c;
}
}
服务层:
package zzz;
public class zhuceService {
ZhuceDao str=new ZhuceDao();
public boolean add(String table,zhucexinxi en)
{
return str.add(table, en);
}
}
事情是这样的,
>>> import urllib
>>> urllib.parse
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module ‘urllib‘ has no attribute ‘parse‘
但是 import urllib.parse就没有问题了
同样的还有logging.config,实在是不知道为什么,难道实际上并不是在同一个包中的么
有空再看吧
(01)分数规划不能直接套板子了,窝一开始想着用什么简单的方法缩点(每个点只有一个入度啊)然后跑拓扑图求解(保存每个点的最优值,通过牛顿迭代减少运算次数),问题是这样还要考虑人数,可不可做都不知道。转念一想,既然每个点只有一个入度,那么环从(0)号点(jyy(orz))开始是肯定无法到达的(无法从任何一个点进入环)。题目没说不可行就一定是可行的(雾),直接从(0)号点遍历即可,自然而然的拓扑图且还有个容易操作的性质,任意两条路不会汇合,理由如上,路只能分散,这不就是颗树吗?实现的话就是选取没有出度的点(距离要大于等于(K)),将唯一路径上的点拎出来跑板子即可。我写了二分,同时小数计算写进了比较函数中,所以常数略大请见谅。
问题来了,这题如果加强一下,每个候选人可能需要好多人推荐还能做吗?
最近在做一个项目,需要安装Redhat的操作系统,但是在redhat官网上下载实在是费劲,速度慢不说,重要的是下载到最后还会出现终止下载的情况,真的很让人头疼,于是想找一下国内是否有相同的ISO镜像,但是需要验证ISO的安全性,否则如果镜像中被植入病毒或者***,那安装过后岂不拜拜了~最直接有效的方法就是对比ISO镜像的hash值是否于官网上提供的一样,那怎么操作呢?下面就简单的介绍一下。一般官网上会提供ISO镜像的hash值如下:
后面我们要做的就是取得你自己下载的ISO镜像的hash值,和上面的做对比就OK了,如果相同则来自官网的下载源,如果不同,则慎用,除非你知道更改过什么。那么如何获得ISO的hash值呢?其实windows操作系统就自带了一个获取iso镜像的命令,那就是certutil。详细说明如下:以管理员身份运行cmd:
注意:路径已经是绝对路径。它可以校验MD5、SHA1、SHA256等多种加密算法的文件。然后要做的就是对这两个字符串做对比,经过对比我的镜像和官网上的相同,可以放心使用了,如果不同,那就要注意了!!!希望可以帮到大家,多谢~
参考文档:
网站性能优化对于大型网站来说非常重要,一个网站的访问打开速度影响着用户体验度,网站访问速度慢会造成高跳出率,小网站很好解决,那对于大型网站由于栏目多,图片和图像都比较庞大,那该怎么进行整体性能优化呢?本文为你提供一份大型php网站性能和并发访问优化方案.
一、大型网站性能提高策略:
大型网站,比如门户网站,在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。这几个解决思路在一定程度上意味着更大的投入。
web容器是一种服务程序,在服务器一个端口就有一个提供相应服务的程序,而这个程序就是处理从客户端发出的请求,如JAVA中的Tomcat容器,ASP的IIS或PWS都是这样的容器。一个服务器可以多个容器。
1、HTML静态化
其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。
2、图片服务器分离
大家知道,对于Web服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的、甚至很多台的图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃。
在应用服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持、尽可能少的LoadModule,保证更高的系统消耗和执行效率。
3、数据库集群、库表散列
大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。
在数据库集群方面,很多数据库都有自己的解决方案,常用的MySQL提供的Master/Slave也是类似的方案。
集群通常会使用CDN与GSBL与DNS负载均衡技术,每个地区一组前台服务器群,例如:网易,百度使用了DNS负载均衡技术,每个频道一组前台服务器,一搜使用了DNS负载技术,所有频道共用一组前台服务器集群。
库表散列是常用并且最有效的解决方案。
我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。
sohu的论坛就是采用了这样的架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。
4、缓存
缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。
架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。
网站程序开发方面的缓存,Linux上提供的Memory Cache是常用的缓存接口,可以在web开发中使用,比如用Java开发的时候就可以调用MemoryCache对一些数据进行缓存和通讯共享,一些大型社区使用了这样的架构。另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块,Java就更多了,.net不是很熟悉,相信也肯定有。
5、镜像
镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异,比如ChinaNet和EduNet之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实时更新。
6、负载均衡
负载均衡将是大型网站解决高负荷访问和大量并发请求采用的高端解决办法。
负载均衡技术发展了多年,有很多专业的服务提供商和产品可以选择,我个人接触过一些解决方法,其中有两个架构可以给大家做参考
二、PHP代码编写优化:
1. echo比print要快很多。
两个方法都会在页面上打印东西,不过echo不返回任何值,print会在成功或失败的时候返回0或1。
2. include_once比include更加耗时。
因为它需要去检查你要包含的class是否已经被包含进来。
3. 对于长段落的字符串一定要使用单引号,而不是双引号。
因为双引号会去搜索字符串中的变量。比如:echo ‘This is long string’.$name就比echo “This is long string $name”要快很多。
4. 不要再循环中嵌套使用for循环
5. 如果能将函数定义为静态的
那么就不要定义成为成员函数,静态函数比成员函数快33%。
6. 如果你可以不通过正则表达式就能解决问题
那么就别用正则。正则表达式比PHP原生的函数要慢一些。
例如使用str_replace取代preg_replae。
7. 尽量不要使用相对路径来包含文件
在相对路径中查找文件,会现在当前目录中查找,然后依次再查找。这样查找文件就很 慢。最好是先定义WEB_ROOT这样的常量,然后使用这个常量来包含文件。
8. 全等符号===比相等==要快
而且if(1 == ’1′)会返回true,if(0 == ”)也会返回true,而当你使用全等符号的时候if(1 ===’1′)和if(0===”)都会返回false。所以当你在程序中需要检测一些布尔变量 的时候最好使用全等符号。
三、针对thinkphp 有以下几种方式
1. 关闭调试模式
由于关闭调试模式之后,系统会自动生成项目编译缓存以及关闭日志写 入,这样可以减少很多的IO加载和日志写入的开销。
2. 开启页面压缩输出
3.1版本开始,增加了OUTPUT_ENcode配置参数,用于控制页面压缩输出。
3. 开启缓存
在网站部署环境安装APC或者Xcache缓存能够有效提升网站运行性能和内存占用
XCache 是一个开源的 opcode 缓存器/优化器, 这意味着他能够提高您服务器上 的 PHP 性能. 他通过把编译 PHP 后的数据缓冲到共享内存从而避免重复的编译 过程, 能够直接使用缓冲区已编译的代码从而提高速度. 通常能够提高您的页面生 成速率 2 到5 倍, 降低服务器负载.
Alternative PHP Cache (APC)是一种对PHP有效的开放源高速缓冲储存器工具,它能够缓存opcode的php中间码。
4. 字段缓存
默认情况下,字段缓存是自动生成的,在开发完成之后,基本上数据库的变动变得 比较少,因此可以考虑合并字段缓存到对应的模型类,这样能够减少每次读取字段 缓存的IO开销。合并的方法是在Runtime/Data/_fields下面找到对应的字段缓存文件
四、 数据库优化
1、选择正确的存储引擎
以 MySQL为例,包括有两个存储引擎 MyISAM 和 InnoDB,每个引擎都有利有弊。
MyISAM 适合于一些需要大量查询的应用。InnoDB 的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比 MyISAM 还慢。但是它支持“行锁” ,支持事务。
2、优化字段的数据类型
记住一个原则,越小的列会越快。对于大多数的数据库引擎来说,硬盘操作可能是最重大的瓶颈。所以,把你的数据变得紧凑会对这种情况非常有帮助,因为这减少了对硬盘的访问。
如果一个表只会有几列罢了(比如说字典表,配置表),那么,我们就没有理由使用 INT 来做主键,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 会更经济一些。如果你不需要记录时间,使用 DATE 要比 DATETIME 好得多。当然,你也需要留够足够的扩展空间。
3、为搜索字段添加索引
索引并不一定就是给主键或是唯一的字段。如果在你的表中,有某个字段你总要会经常用来做搜索,那么最好是为其建立索引,除非你要搜索的字段是大的文本字段,那应该建立全文索引。
4、避免使用Select *从数据库里读出越多的数据,
那么查询就会变得越慢。并且,如果你的数据库服务器和WEB服务器是两台独立的服务器的话,这还会增加网络传输的负载。即使你要查询数据表的所有字段,也尽量不要用*通配符,善用内置提供的字段排除定义也许能给带来更多的便利。
5、使用 ENUM 而不是 VARCHAR
ENUM 类型是非常快和紧凑的。在实际上,其保存的是 TINYINT,但其外表上显示为字符串。这样一来,用这个字段来做一些选项列表变得相当的完美。例如,性别、民族、部门和状态之类的这些字段的取值是有限而且固定的,那么,你应该使用 ENUM 而不是 VARCHAR。
6、尽可能的使用 NOT NULL
除非你有一个很特别的原因去使用 NULL 值,你应该总是让你的字段保持 NOT NULL。 NULL其实需要额外的空间,并且,在你进行比较的时候,你的程序会更复杂。 当然,这里并不是说你就不能使用NULL了,现实情况是很复杂的,依然会有些情况下,你需要使用NULL值。
7、固定长度的表会更快
如果表中的所有字段都是“固定长度”的,整个表会被认为是 “static” 或 “fixed-length”。 例如,表中没有如下类型的字段: VARCHAR,TEXT,BLOB。只要你包括了其中一个这些字段,那么这个表就不是“固定长度静态表”了,这样,MySQL 引擎会用另一种方法来处理。
固定长度的表会提高性能,因为MySQL搜寻得会更快一些,因为这些固定的长度是很容易计算下一个数据的偏移量的,所以读取的自然也会很快。而如果字段不是定长的,那么,每一次要找下一条的话,需要程序找到主键。
并且,固定长度的表也更容易被缓存和重建。不过,唯一的副作用是,固定长度的字段会浪费一些空间,因为定长的字段无论你用不用,他都是要分配那么多的空间。
使用“垂直分割”技术,你可以分割你的表成为两个一个是定长的,一个则是不定长的。
8、垂直分割
“垂直分割”是一种把数据库中的表按列变成几张表的方法,这样可以降低表的复杂度和字段的数目,从而达到优化的目的。
例如:在User表中有一个字段是家庭地址,这个字段是可选字段,相比起,而且你在数据库操作的时候除了个人信息外,你并不需要经常读取或是改写这个字段。那么,为什么不把他放到另外一张表中呢? 这样会让你的表有更好的性能,大家想想是不是,大量的时候,我对于用户表来说,只有用户ID,用户名,口令,用户角色等会被经常使用。小一点的表总是会有好的性能。
另外,你需要注意的是,这些被分出去的字段所形成的表,你不会经常性地去Join他们,不然的话,这样的性能会比不分割时还要差,而且,会是极数级的下降。
9、EXPLAIN 你的 SELECT 查询;
使用 EXPLAIN 关键字可以让你知道MySQL是如何处理你的SQL语句的。这可以帮你分析你的查询语句或是表结构的性能瓶颈。EXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的,你的数据表是如何被搜索和排序的……等等,等等。
通常我们可以对比较复杂的尤其是涉及到多表的SELECT语句,把关键字EXPLAIN加到前面。你可以使用phpmyadmin来做这个事。详情见附件explain.doc.
五、前端优化
优化完后端和数据库之后,我们紧接着要做的就是针对输出的页面优化你的前端页面和资源文件,主要包括对图片、JS和样式文件的优化。
我们建议采用下列网页性能测试工具进行检测和分析,会给出相关的优化建议:
PageSpeed 谷歌开发的工具
网站管理员和网络开发人员可以使用PageSpeed来评估他们网页的性能,并获得有关如何改进性能的建议。
yslow YSlow
可以对网站的页面进行分析,并告诉你为了提高网站性能,如何基于某些规则而进行优化。
本文转载于:php中文网
if ($request->hasFile(‘banner_img‘) && $request->file(‘banner_img‘)->isValid()) {//判断文件是否存在
$file = $request->file(‘banner_img‘);
$ext = $file->extension();
$filesize = $_FILES[‘banner_img‘][‘size‘];
$filename = md5(time().rand(100000,999999)).‘.‘.$ext;
$store_result = $request->file(‘banner_img‘)->move(‘https://www.gxlsystem.com/upload‘,$filename);//上传文件
if(!in_array(strtoupper($ext),[‘JPG‘,‘JPEG‘,‘PNG‘,‘GIF‘])){
return [
‘code‘=>100000,
‘msg‘=>‘图片格式不正确‘.strtoupper($ext),
‘data‘=>null
];
}
if(!$filesize || $filesize>1048576 * 20){
return [
‘code‘=>100000,
‘msg‘=>‘检测到文件大小为0或文件大小超过20M‘,
‘data‘=>null
];
}
return response()->json([
‘code‘ =>0,
‘msg‘ =>‘ok‘,
‘data‘=>‘https://www.gxlsystem.com/upload‘.$filename
]);
}
if ($request->hasFile(‘banner_img‘) && $request->file(‘banner_img‘)->isValid()) {//判断文件是否存在
$log = $request->file(‘banner_img‘);
$arr = explode(‘.‘, $_FILES[‘banner_img‘][‘name‘]);
$ext = $arr[count($arr) - 1];
$filename = md5(time().rand(100000,999999)).‘.‘.$ext;
$filesize = $_FILES[‘banner_img‘][‘size‘];
if(!in_array(strtoupper($ext),[‘JPG‘,‘JPEG‘,‘PNG‘,‘GIF‘])){
return [
‘code‘=>100000,
‘msg‘=>‘图片格式不正确‘.strtoupper($ext),
‘data‘=>null
];
}
if(!$filesize || $filesize>1048576 * 20){
return [
‘code‘=>100000,
‘msg‘=>‘检测到文件大小为0或文件大小超过20M‘,
‘data‘=>null
];
}
$store_result = $log->storeAs(‘banner‘,$filename,‘banner_public‘);
$output = [
// ‘extension‘ =>$extension,
‘store_result‘ =>$store_result
];
return [
‘code‘=>0,
‘msg‘=>‘ok‘,
‘data‘=>$store_result
];
}
.Net Core 3.0已经来了,WTM怎么可以落后呢。最新发布的WTM2.3.9版本已经支持.Net Core 3.0啦,现在在线生成项目的时候可以选择2.2和3.0两个版本。小伙伴们快来体验吧。
WTM 2.3.9另一个重大变化是增加了对多语言的支持,这也是为了WTM国际化做好准备。
国外使用dotnet core的人还是很多的,WTM曾经至少有三次在GitHub总趋势榜上出现,
但是我发现大量的国外用户满心欢心的点进来发现满屏的中文,他们就。。。走。。。了。。。
所以近期我们加大了国际化的力量,这次2.3.9已经把过去所有框架内部写死的中文改成了通过资源文件来支持多语言,
下一步是增加github说明的英文版和文档网站的英文版,以及代码注释的翻译。
另外好多使用WTM来做CMS系统的朋友一直抱怨富文本太弱了,这回我们把UEditor集成进来了,要做CMS的可以动手了!
沉痛哀悼
负责VUE版本开发的龙哥,就业于国内某知名智能电动汽车公司,前不久遭遇大裁员。
龙哥凭借过硬的技术被留了下来,以前三个人干的活,现在他一个人干,天天累成狗。
不过我会督促他的,吹过的牛逼无论如何都要兑现!
精彩预告:
JWT将很快集成进WTM,朋友们再等等,也就一两周吧。
WTM框架地址: https://wtmdoc.walkingtec.cn/
End
转化流程:先将输入流转为String类型,再使用alibaba的json转换工具,将字符串转化为json数组
SensorDevices sensorDevices = new SensorDevices();
request.setCharacterEncoding("utf-8");
JSONObject json = new JSONObject();
JSONArray list = new JSONArray();
BufferedReader br;
String line = null;
String brStr = null;
String jsonStr = null;
String jsonStr2 = null;
try {
br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));
StringBuilder sb = new StringBuilder();
// line = br.readLine();
while ((line = br.readLine()) != null) {
sb.append(line);
brStr = sb.toString();
}
list = JSONArray.parseArray(brStr);
} catch (IOException e) {
e.printStackTrace();
}
// System.out.println("+++++++++这是brStr:" + brStr);
// System.out.println("+++++++++这是JSON:" + json);
// 遍历json数组
System.out.println("+++++++++这是JSONArray:" + list);
1、引用
<link rel="stylesheet" href="https://www.gxlsystem.com/static/css/jquery.mCustomScrollbar.css" />
<link rel="stylesheet" href="https://www.gxlsystem.com/static/plug/jQuery-moji/src/css/jquery.emoji.css"/>
<script src="https://www.gxlsystem.com/static/js/jquery-3.4.1.js" type="text/javascript" charset="utf-8"></script>
<script src="https://www.gxlsystem.com/static/js/jquery.mCustomScrollbar.js"></script>
<script src="https://www.gxlsystem.com/static/plug/jQuery-emoji/lib/script/jquery.mousewheel-3.0.6.min.js"></script>
<script src="https://www.gxlsystem.com/static/plug/jQuery-emoji/src/js/jquery.emoji.js"></script>
2、html
<div class="windows_input" id="talkbox">
<div class="input_icon">
<a href="https://www.gxlsystem.com/javascript:;"></a>
</div>
<div class="input_box">
<textarea name="" rows="" cols="" id="input_box"></textarea>
<p title="刘晴"><span>‘+text.value+‘</span></li>‘;
text.value = ‘‘;
chat.scrollTop=chat.scrollHeight;
};
};
document.onkeyup = function(e){
var e = e || event;
if( e.keyCode == 13 && e.ctrlKey ){
if(text.value ==‘‘){
alert(‘不能发送空消息‘);
}else{
text.value = replace_em(text.value);
chat.innerHTML += ‘<li class="me"><p class="time">今天 18:26</p><img src="https://www.gxlsystem.com/‘+‘https://tva4.sinaimg.cn/crop.0.0.1080.1080.50/005Sqdykjw8epvbgog754j30u00u0jtt.jpg?KID=imgbed,tva&Expires=1571046485&ssig=3tTYpfvSOI‘+‘" title="刘晴"><span>‘+text.value+‘</span></li>‘;
text.value = ‘‘;
chat.scrollTop=chat.scrollHeight;
};
}
}
//表情格式替换
function replace_em(str){
str = str.replace(/</g,‘<‘);
str = str.replace(/>/g,‘>‘);
str = str.replace(/
/g,‘<br/>‘);
str = str.replace(/[qq_([0-9]*)]/g,"<img src=https://www.gxlsystem.com/‘/static/plug/jQuery-emoji/dist/img/qq/$1.gif‘ />");
str = str.replace(/[em_([0-9]*)]/g,"<img src=https://www.gxlsystem.com/‘/static/plug/jQuery-emoji/dist/img/tieba/$1.jpg‘ />");
str = str.replace(/[other_([0-9]*)]/g,"<img src=https://www.gxlsystem.com/‘/static/plug/jQuery-emoji/dist/img/tieba/$1.jpg‘ />");
return str;
}
};
send();
Solution [JSOI2008]球形空间产生器
题目大意:在(n)维空间中给定(n + 1)个点,求到(n + 1)个点的欧几里得距离相等的点
高斯消元
分析:
假设我们有点((x_1,x_2,x_3, dots,x_n)),到点((a_1,a_2,a_3, dots,a_n)),((b_1,b_2,b_3, dots,b_n))相等
有(sqrt{sum(x_i-a_i)^2}=sqrt{sum(x_i-b_i)^2})
显然根号下都是非负数
(sum(x_i-a_i)^2=sum(x_i-b_i)^2)
平方差
(sum(2x_i-a_i-b_i)(b_i-a_i)=0)
展开式子
(sum(-2a_ix_i+2b_ix_i+{a_i}^2-{b_i}^2)=0)
整理得
(sum x_i cdot (-2a_i+2b_i)=sum({b_i}^2-{a_i}^2))
然后这就是一个(n)元一次方程了,我们可以(n^2)求出(n)元一次方程组,然后对这个方程组进行高斯消元即可,为了数值稳定性每次选取绝对值最大的作为主元,取前(n)项回带即可
(Trick:)一个(n)维的数组实际上每个元素都是指针,指向(n-1)维的数组,(0)维自然就是元素啦,因此我们可以直接交换指针,做到常数时间内交换方程组两项
又一个(Trick:)输出调试信息可以用(cerr),以避免忘记删除调试信息而(GG)
前期准备
引入phpqrcode类库(下载地址:;支持彩色二维码的下载地址:)
PHP开启GD扩展库支持
1、利用phpqrcode生成二维码:
原理分析:
下载下来的类文件是一个压缩包,包含很多文件和演示程序,我们只需要里边的phpqrcode.php
这一个文件就可以生成二维码了,它是一个多个类的集合文件,我们需要用到里边的QRcode类的png()方法:
//测试生成带头像的网站二维码海报
public function ce_haibao_qrcode(){
$codeurl = "https://www.baidu.com";
Vendor(‘PHPQRcode.class#phpqrcode‘);
$logoQR = "http://thirdwx.qlogo.cn/mmopen/vi_32/icaYhiapVcmsyGnHouHeSvYiaz8yxtvfBicgx5x8joGh4uNiaibp8skQf8Uv4CNtibsJDndbOQwI9LSvDQP6slFQaLy4g/132";
$level=3;
$size=6;
$errorCorrectionLevel =intval($level) ;//容错级别
$matrixPointSize = intval($size);//生成图片大小
$dirPath =‘https://www.gxlsystem.com/Uploads/qrcode/‘.date(‘Y-m-d‘).‘/‘;//保存二维码路径
$dirPath_B =‘/Uploads/qrcode/‘.date(‘Y-m-d‘).‘/‘;//保存二维码路径
if(!file_exists($dirPath)){
mkdir($dirPath, 0777);
}
$tmpName = time().".png"; //保存在服务器上的二维码名称
$qrcodeName = $dirPath .$tmpName; //保存在服务器上的二维码路径
$qrcodeName_B = $dirPath_B .$tmpName; //保存在服务器上的二维码路径
//生成二维码图片
$object = new QRcode();
$object->png($codeurl, $qrcodeName, $errorCorrectionLevel, $matrixPointSize, 2);
//$QR = imagecreatefrompng($qrcodeName);
$QR = $qrcodeName;
$QRlogoPath = ‘https://www.gxlsystem.com/Uploads/qrcode/qrlogo/‘.date(‘Y-m-d‘).‘/‘;
$QRlogoPath_B = ‘/Uploads/qrcode/qrlogo/‘.date(‘Y-m-d‘).‘/‘;
if(!file_exists($QRlogoPath)){
mkdir($QRlogoPath, 0777);
}
$qrcode = time().‘.png‘;
$QRlogo = $QRlogoPath .$qrcode;
$QRlogo_B = $QRlogoPath_B .$qrcode;
if($logoQR !==FALSE){
$QR = imagecreatefromstring(file_get_contents($QR));
$logo = imagecreatefromstring(file_get_contents($logoQR));
$QR_width = imagesx($QR);//二维码图片宽度
$QR_height = imagesy($QR);//二维码图片高度
$logo_width = imagesx($logo);//logo图片宽度
$logo_height = imagesy($logo);//logo图片高度
$logo_qr_width = $QR_width / 5;
$scale = $logo_width/$logo_qr_width;
$logo_qr_height = $logo_height/$scale;
$from_width = ($QR_width - $logo_qr_width) / 2;
//重新组合图片并调整大小
imagecopyresampled($QR, $logo, $from_width, $from_width, 0, 0, $logo_qr_width,
$logo_qr_height, $logo_width, $logo_height);
header(‘Content-type: image/png‘);
//合成带logo的二维码
ImagePng($QR,$QRlogo);
$dst_path = ‘https://www.gxlsystem.com/Uploads/haibao/haibao.png‘;//海报素材底图
$src_path = $QRlogo; //覆盖图,用上面的那张图QRlogo
$haibaoPath = ‘https://www.gxlsystem.com/Uploads/qrcode/haibao/‘.date(‘Y-m-d‘).‘/‘;
//$haibaoPath_B = ‘/Uploads/qrcode/haibao/‘.date(‘Y-m-d‘).‘/‘;
if(!file_exists($haibaoPath)){
mkdir($haibaoPath, 0777);
}
$haibao = time().‘.png‘;
$haibao = $haibaoPath .$haibao;
//$haibao_B = $haibaoPath_B .$haibao;
//创建图片实例
$dst = imagecreatefromstring(file_get_contents($dst_path));//海报
$src = imagecreatefromstring(file_get_contents($src_path));//二维码
//获取覆盖图的宽高
list($src_w, $src_h) = getimagesize($src_path);
//获取海报的宽高
list($dst_w, $dst_h) = getimagesize($dst_path);
// imagecopymerge($dst, $src, 20, 120, 0, 0, $src_w, $src_h, 100);
imagecopymerge($dst, $src, ($dst_w-$src_w)/2, $dst_h-100-$src_h, 0, 0, $src_w, $src_h, 100);
list($dst_w, $dst_h) = getimagesize($dst_path);
imagepng($dst,$haibao);//生成图片并保存到服务器上 合成的海报
imagedestroy($dst);
imagedestroy($src);
$haibao = substr($haibao,1);//去掉左边第一个小点
$arr[‘msg‘] = $haibao;
echo json_encode($arr);exit;
}
}
有关帝国CMS新版防火墙介绍可以查看:
本文为大家讲解如何使用网站防火墙:
一、配置“网站防火墙”有下面两种方法:
1、后台>“系统设置”>“网站防火墙”。
2、修改e/class/config.php文件配置。
二、下面讲解一下相关设置的作用和使用:
1、防火墙加密密钥:
此项必须设置,填写10~50个任意字符,最好多种字符组合。
并且建议每星期或每个月变更一次。
2、允许后台登陆的域名:
设置只允许访问后台的域名,域名绑定到网站根目录,只有通过这个域名访问e/admin后台才是被允许的。域名一般用网站的二级域名即可,如果为了更保险也可绑定一个全新域名的二级域名。比如:网站域名:,而访问后台域名则用
并且绑定的域名还支持增加端口,比如:,前提是服务器支持使用这个端口访问网站。
绑定域名()后的访问后台地址是:,而通过其他域名访问后台都是空白。
3、允许登陆后台的时间点、允许登陆后台的星期:
方便工作时间工作的单位设置,使网站安全维护更容易把控,不让用户在工作时间外进入后台。
如果紧急事件例外可以手动修改e/class/config.php文件配置。
4、防火墙后台预登陆验证变量名和防火墙后台预登陆认证码
这两项必须设置。
预登陆验证变量名:可由英文字母+数字组成(必须是字母开头),5~20个字符组成。
预登陆认证码:填写10~50个任意字符,最好多种字符组合。
并且建议每星期或每个月变更一次。
5、屏蔽提交敏感字符:
此功能是安全防火墙的核心,可对前台用户所有录入的信息进行安全过滤。通常设置php、mysql等攻击的相关字符。
比如:sql注入通常会用到的字符:select,outfile,union,delete,insert,update,replace,sleep,benchmark,load_file,create
相关链接:
现在通过 PDO 连接上了,在开始进行查询前,必须先理解 PDO 是如何管理事务的。
事务支持四大特性(ACID):
原子性(Atomicity)
一致性(Consistency)
隔离性(Isolation)
持久性(Durability)
通俗地讲,在一个事务中执行的任何操作,即使是分阶段执行的,也能保证安全地应用于数据库,并在提交时不会受到来自其他连接的干扰。
事务操作也可以根据请求自动撤销(假设还没有提交),这使得在脚本中处理错误更加容易。
事务通常是通过把一批更改"积蓄"起来然后使之同时生效而实现的;这样做的好处是可以大大地提供这些更改的效率。
换句话说,事务可以使脚本更快,而且可能更健壮(不过需要正确地使用事务才能获得这样的好处)。
不幸的是,并非每种数据库都支持事务,因此当第一次打开连接时,PDO 需要在所谓的"自动提交"模式下运行。
自动提交模式意味着,如果数据库支持,运行的每个查询都有它自己的隐式事务,如果数据库不支持事务,则没有。
如果需要一个事务,则必须用 PDO::beginTransaction() 方法来启动。如果底层驱动不支持事务,则抛出一个 PDOException 异常(不管错误处理设置是怎样的,这都是一个严重的错误状态)。
一旦开始了事务,可用 PDO::commit() 或 PDO::rollBack()来完成,这取决于事务中的代码是否运行成功。
注意: PDO 仅在驱动层检查是否具有事务处理能力。如果某些运行时条件意味着事务不可用,且数据库服务接受请求去启动一个事务,PDO::beginTransaction() 将仍然返回 TRUE 而且没有错误。 试着在 MySQL 数据库的 MyISAM 数据表中使用事务就是一个很好的例子。
当脚本结束或连接即将被关闭时,如果尚有一个未完成的事务,那么 PDO 将自动回滚该事务。这种安全措施有助于在脚本意外终止时避免出现不一致的情况——如果没有显式地提交事务,那么假设是某个地方出错了,所以执行回滚来保证数据安全。
注意: 只有通过 PDO::beginTransaction() 启动一个事务后,才可能发生自动回滚。如果手动发出一条查询启动事务, 则 PDO 无法知晓,从而在必要时不能进行回滚。
在事务中执行批处理:
在下面例子中,假设为新员工创建一组条目,分配一个为23的ID。除了登记此人的基本数据之外,还需要记录他的工资。
两个更新分别完成起来很简单,但通过封闭在 PDO::beginTransaction() 和PDO::commit() 调用中,可以保证在更改完成之前,其他人无法看到这些更改。
如果发生了错误,catch 块回滚自事务启动以来发生的所有更改,并输出一条错误信息。
<?php
try {
$dbh = new PDO(‘odbc:SAMPLE‘, ‘db2inst1‘, ‘ibmdb2‘,
array(PDO::ATTR_PERSISTENT => true));
echo "Connected
";
} catch (Exception $e) {
die("Unable to connect: " . $e->getMessage());
}
try {
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->beginTransaction();
$dbh->exec("insert into staff (id, first, last) values (23, ‘Joe‘, ‘Bloggs‘)");
$dbh->exec("insert into salarychange (id, amount, changedate)
values (23, 50000, NOW())");
$dbh->commit();
} catch (Exception $e) {
$dbh->rollBack();
echo "Failed: " . $e->getMessage();
}
?>
并不局限于在事务中更改,也可以发出复杂的查询来提取数据,还可以使用那些信息来构建更多的更改和查询;当事务激活时,可以保证其他人在操作进行当中无法作出更改。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.triangle{
width: 0;
height: 0;
border-top: 50px solid black;
border-right: 50px solid transparent;
border-left: 50px solid transparent;
}
</style>
</head>
<body>
<div class="triangle"></div>
</body>
</html>
属性选择器
属性选择器,字面意思就是根据标签中的属性,选中当前的标签。
/*根据属性查找*/
/*[for]{
color: red;
}*/
/*找到for属性的等于username的元素 字体颜色设为红色*/
/*[for=‘username‘]{
color: yellow;
}*/
/*以....开头 ^*/
/*[for^=‘user‘]{
color: #008000;
}*/
/*以....结尾 $*/
/*[for$=‘vvip‘]{
color: red;
}*/
/*包含某元素的标签*/
/*[for*="vip"]{
color: #00BFFF;
}*/
/**/
/*指定单词的属性*/
label[for~=‘user1‘]{
color: red;
}
input[type=‘text‘]{
background: red;
}
伪类选择器
伪类选择器一般会用在超链接a标签中,使用a标签的伪类选择器,我们一定要遵循"爱恨准则" LoVe HAte
/*没有被访问的a标签的样式*/
.box ul li.item1 a:link{
color: #666;
}
/*访问过后的a标签的样式*/
.box ul li.item2 a:visited{
color: yellow;
}
/*鼠标悬停时a标签的样式*/
.box ul li.item3 a:hover{
color: green;
}
/*鼠标摁住的时候a标签的样式*/
.box ul li.item4 a:active{
color: yellowgreen;
}
css3的选择器nth-child()
/*选中第一个元素*/
div ul li:first-child{
font-size: 20px;
color: red;
}
/*选中最后一个元素*/
div ul li:last-child{
font-size: 20px;
color: yellow;
}
/*选中当前指定的元素 数值从1开始*/
div ul li:nth-child(3){
font-size: 30px;
color: purple;
}
/*n表示选中所有,这里面必须是n, 从0开始的 0的时候表示没有选中*/
div ul li:nth-child(n){
font-size: 40px;
color: red;
}
/*偶数*/
div ul li:nth-child(2n){
font-size: 50px;
color: gold;
}
/*奇数*/
div ul li:nth-child(2n-1){
font-size: 50px;
color: yellow;
}
/*隔几换色 隔行换色
隔4换色 就是5n+1,隔3换色就是4n+1
*/
div ul li:nth-child(5n+1){
font-size: 50px;
color: red;
}
伪元素选择器
/*设置第一个首字母的样式*/
p:first-letter{
color: red;
font-size: 30px;
}
/* 在....之前 添加内容 这个属性使用不是很频繁 了解 使用此伪元素选择器一定要结合content属性*/
p:before{
content:‘alex‘;
}
/*在....之后 添加内容,使用非常频繁 通常与咱们后面要讲到布局 有很大的关联(清除浮动)*/
p:after{
content:‘&‘;
color: red;
font-size: 40px;
}
一 Web应用的组成
接下来我们学习的目的是为了开发一个Web应用程序,而Web应用程序是基于B/S架构的,其中B指的是浏览器,负责向S端发送请求信息,而S端会根据接收到的请求信息返回相应的数据给浏览器,需要强调的一点是:S端由server和application两大部分构成,如图所示:
上图:Web应用组成
二 开发一个Web应用
我们无需开发浏览器(本质即套接字客户端),只需要开发S端即可,S端的本质就是用套接字实现的,如下
目前S端已经可以正常接收浏览器发来的请求消息了,但是浏览器在接收到S端回复的响应消息b‘hello world‘时却无法正常解析 ,因为浏览器与S端之间收发消息默认使用的应用层协议是HTTP,浏览器默认会按照HTTP协议规定的格式发消息,而S端也必须按照HTTP协议的格式回消息才行,所以接下来我们详细介绍HTTP协议
HTTP协议详解链接地址:
S端修订版本:处理HTTP协议的请求消息,并按照HTTP协议的格式回复消息
此时,重启S端后,再在客户端浏览器输入: 便可以看到正常结果hello world了。
我们不仅可以回复hello world这样的普通字符,还可以夹杂html标签,浏览器在接收到消息后会对解析出的html标签加以渲染
更进一步我们还可以返回一个文件,例如timer.html,内容如下
S端程序如下
上述S端为浏览器返回的都是静态页面(内容都固定的),我们还可以返回动态页面(内容是变化的)
三 Web框架的由来
综上案例我们可以发现一个规律,在开发S端时,server的功能是复杂且固定的(处理socket消息的收发和http协议的处理),而app中的业务逻辑却各不相同(不同的软件就应该有不同的业务逻辑),重复开发复杂且固定的server是毫无意义的,有一个wsgiref模块帮我们写好了server的功能,这样我们便只需要专注于app功能的编写即可
timer.html已经存在了,新增的index.html页面内容如下:
上述案例中app在处理业务逻辑时需要根据不同的url地址返回不同的页面内容,当url地址越来越多,需要写一堆if判断,代码不够清晰,耦合程度高,所以我们做出以下优化
随着业务逻辑复杂度的增加,处理业务逻辑的函数以及url_patterns中的映射关系都会不断地增多,此时仍然把所有代码都放到一个文件中,程序的可读性和可扩展性都会变得非常差,所以我们应该将现有的代码拆分到不同文件中
插图:
views.py 内容如下:
urls.py内容如下:
main.py 内容如下:
至此,我们就针对application的开发自定义了一个框架,所以说框架的本质就是一系列功能的集合体、不同的功能放到不同的文件中。有了该框架,可以让我们专注于业务逻辑的编写,极大的提高了开发web应用的效率(开发web应用的框架可以简称为web框架),比如我们新增一个业务逻辑,要求为:浏览器输入http://127.0.0.1:8011/home 就能访问到home.html页面,在框架的基础上具体开发步骤如下:
步骤一:在templates文件夹下新增home.html
步骤二:在urls.py的url_patterns中新增一条映射关系
步骤三:在views.py中新增一个名为home的函数
我们自定义的框架功能有限,在Python中我们可以使用别人开发的、功能更强大的Django框架
四 Django框架的安装与使用
在使用Django框架开发web应用程序时,开发阶段同样依赖wsgiref模块来实现Server的功能,我们使用Django框架是为了快速地开发application
4.1 安装
目前在企业开发中Django框架使用的主流版本为1.11.x版本,最新版本为2.x,我们主要讲解1.11版本,同时会涉及2.x的新特性
4.2 使用
4.2.1 快速创建并启动Django项目
如果使用的是我们自定义的框架来开发web应用,需要事先生成框架包含的一系列基础文件,然后在此基础上进行开发。
如果使用的是Django框架来开发web应用,同样需要事先生成Django框架包含的一系列基础文件,然后在此基础上进行开发。
但Django框架更为方便的地方在于它已经为我们提供了一系列命令来帮我们快速地生成这一系列基础文件
创建功能模块
运行
4.2.2 Django项目目录结构
截目录树的图(按照下述目录截图)
关键文件介绍
4.2.3 基于Pycharm创建Django项目
4.2.4 基于Django实现的一个简单示例
(1)url.py
(2)视图
(3)模版
在templates目录下新建文件index.html
测试:
4.2.5 Django框架的分层与请求生命周期
综上,我们使用Django框架就是为了开发application,而application的工作过程本质就是根据不同的请求返回不同的数据,Django框架将这个工作过程细分为如下四层去实现
1、路由层(根据不同的地址执行不同的视图函数,详见urls.py)
2、视图层(定义处理业务逻辑的视图函数,详见views.py)
3、模型层 (跟数据库打交道的,详解models.py)
4、模板层(待返回给浏览器的html文件,详见templates)
django请求生命周期
这体现了一种解耦合的思想,下面我们开始详细介绍每一层
1234567891011121314
T('Public/menu');// 返回 当前模块/View/Public/menu.htmlT('blue/Public/menu');// 返回 当前模块/View/blue/Public/menu.htmlT('Public/menu','Tpl');// 返回 当前模块/Tpl/Public/menu.htmlT('Public/menu');// 如果TMPL_FILE_DEPR 为 _ 返回 当前模块/Tpl/Public_menu.htmlT('Public/menu');// 如果TMPL_TEMPLATE_SUFFIX 为.tpl 返回 当前模块/Tpl/Public/menu.tplT('Admin@Public/menu');// 返回 Admin/View/Public/menu.htmlT('Extend://Admin@Public/menu');// 返回 Extend/Admin/View/Public/menu.html (Extend目录取决于AUTOLOAD_NAMESPACE中的配置)
一、动画
1. 动画
1. 三种方式显示和隐藏元素
1. 默认显示和隐藏方式
1. show([speed,[easing],[fn]])
1. 参数:
1. speed:动画的速度。三个预定义的值("slow","normal", "fast")或表示动画时长的毫秒数值(如:1000)
2. easing:用来指定切换效果,默认是"swing",可用参数"linear"
* swing:动画执行时效果是 先慢,中间快,最后又慢
* linear:动画执行时速度是匀速的
3. fn:在动画完成时执行的函数,每个元素执行一次。
2. hide([speed,[easing],[fn]])
3. toggle([speed],[easing],[fn])
2. 滑动显示和隐藏方式
1. slideDown([speed],[easing],[fn])
2. slideUp([speed,[easing],[fn]])
3. slideToggle([speed],[easing],[fn])
3. 淡入淡出显示和隐藏方式
1. fadeIn([speed],[easing],[fn])
2. fadeOut([speed],[easing],[fn])
3. fadeToggle([speed,[easing],[fn]])
----------------------
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="https://www.gxlsystem.com/js/jquery-3.3.1.min.js"></script>
<script>
//隐藏div
function hideFn(){
/* $("#showDiv").hide("slow","swing",function(){
alert("隐藏了...")
});*/
/*
//默认方式
$("#showDiv").hide(5000,"swing");
*/
/*
//滑动方式
$("#showDiv").slideUp("slow");
*/
//淡入淡出方式
$("#showDiv").fadeOut("slow");
}
//显示div
function showFn(){
/*$("#showDiv").show("slow","swing",function(){
alert("显示了...")
});*/
/*
//默认方式
$("#showDiv").show(5000,"linear");
*/
/*
//滑动方式
$("#showDiv").slideDown("slow");
*/
//淡入淡出方式
$("#showDiv").fadeIn("slow");
}
//切换显示和隐藏div
function toggleFn(){
/*
//默认方式
$("#showDiv").toggle("slow");
*/
/*
//滑动方式
$("#showDiv").slideToggle("slow");
*/
//淡入淡出方式
$("#showDiv").fadeToggle("slow");
}
</script>
</head>
<body>
<input type="button" value="点击按钮隐藏div" onclick="hideFn()">
<input type="button" value="点击按钮显示div" onclick="showFn()">
<input type="button" value="点击按钮切换div显示和隐藏" onclick="toggleFn()">
<div id="showDiv" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
/*
遍历
1. js的遍历方式
* for(初始化值;循环结束条件;步长)
2. jq的遍历方式
1. jq对象.each(callback)
2. $.each(object, [callback])
3. for..of:jquery 3.0 版本之后提供的方式
*/
$(function () {
//1.获取所有的ul下的li
var citys = $("#city li");
/* //2.遍历li
for (var i = 0; i < citys.length; i++) {
if("上海" == citys[i].innerHTML){
//break; 结束循环
//continue; //结束本次循环,继续下次循环
}
//获取内容
alert(i+":"+citys[i].innerHTML);
}*/
/*
//2. jq对象.each(callback)
citys.each(function (index,element) {
//3.1 获取li对象 第一种方式 this
//alert(this.innerHTML);
//alert($(this).html());
//3.2 获取li对象 第二种方式 在回调函数中定义参数 index(索引) element(元素对象)
//alert(index+":"+element.innerHTML);
//alert(index+":"+$(element).html());
//判断如果是上海,则结束循环
if("上海" == $(element).html()){
//如果当前function返回为false,则结束循环(break)。
//如果返回为true,则结束本次循环,继续下次循环(continue)
return true;
}
alert(index+":"+$(element).html());
});*/
//3 $.each(object, [callback])
/* $.each(citys,function () {
alert($(this).html());
});*/
//4. for ... of:jquery 3.0 版本之后提供的方式
for(li of citys){
alert($(li).html());
}
});
</script>
</head>
<body>
<ul id="city">
<li>北京</li>
<li>上海</li>
<li>天津</li>
<li>重庆</li>
</ul>
</body>
</html>
目录
一、软件开发架构
C/S 客户端 服务端
B/S 浏览器和服务端
注意:B/S 的本质也是C/S架构
二、实现一个简单的web服务
我们无需开发浏览器(本质即套接字客户端),只需要开发S端即可,S端的本质就是用套接字实现的,如下
注意:以上的S端已经可以正常接收浏览器发来的请求信息了,但是浏览器在接收到S端回复的响应消息b‘hello world’时却不能正常的解析,此处是因为浏览器和S端之间收发消息默认使用的应用层协议是Http协议,浏览器默认以http协议规定的格式发消息,而s端也必须按照http协议的格式回消息才行
2.1 S端修正版本
处理HTTP协议的请求信息,并按照HTTP协议的格式响应回复信息
2.2 S端将html标签返回
2.3 浏览器携带参数访问S端时的处理
2.4 S端返回html页面给浏览器
2.5 减少代码冗余的server端
三、基于wsgrief模块实现简易web服务框架
这个模块实现了上面的所有手动去写原生怠慢的过程
根据每个部分功能不同,将他们分成不同的py文件
urls.py:只用来存放路由与视图函数对应的关系的
View.py:用来存放视图函数(函数、类)
拆分完成后,如果想要给一个应用添加应用,仅仅只需要在urls.py和View.py两个文件里改就可以了
并且使用wsgrief模块的本质是将不用我们自己去写server部分,而且我们是通过任务分发的方式去实现每个浏览器请求信息的,以下就是一个完整的例子
3.1 通过后端获取当前时间展示到前端
3.2 后端通过获取数据库的信息,展示到前端页面
此处提出一个概念:模板的渲染
模板的渲染:后端获取的数据 传递给前端页面就叫做模板的渲染
现在又有一个包可以帮我们实现这个任务了:jinja2
安装方式:pip3 install jinja2
模板语法(极其贴近python后端语法)
?
? #通过获取数据库的数据,然后渲染到前端页面
? {% for user_dict in user_list %}
?
? {{ user_dict.id }}
? {{ user_dict.name }}
? {{ user_dict.pwd }}
?
? {% endfor %}
注意:在这里我们发现,不管我们添加多少个功能,我们的server页面都没有变过,所以这就是我们的web框架
注意2:在我们的功能逐渐变多时,我们此时可能要写很多个html页面,这样会影响到我们调式代码,所以在这里,我们把这些html页面单独拿出来放在一个文件夹里(templates文件夹)
云计算学习路线教程大纲课件:HTTP Server: Apache知识点:
建议使用2.4及以上的版本
========================================================
一、Apache基础
Apache: www.apache.org
软件包: httpd
服务端口: 80/tcp(http) 443/tcp(https,http+ssl)
配置文件: /etc/httpd/conf/httpd.conf
/etc/httpd/conf.d/*.conf
/etc/httpd/conf.d/welcome.conf //默认测试页面
二、安装Apache
[root@apache ~]# yum -y install httpd
[root@apache ~]# systemctl start httpd
[root@apache ~]# systemctl enable httpd
网站主目录建立测试页:
[root@apache ~]# vim /var/www/html/index.html
tianyun
[root@apache ~]# vim /var/www/html/2.php
<?php
phpinfo();
?>
192.168.31.154/index.html
[root@apache ~]# sed -ri ‘/^SELINUX=/cSELINUX=disabled‘ /etc/selinux/config
[root@apache ~]# setenforce 0
[root@apache ~]# firewall-cmd --permanent --add-service=http
[root@apache ~]# firewall-cmd --permanent --add-service=https
[root@apache ~]# firewall-cmd --reload
三、安装PHP
[root@apache ~]# yum -y install php //php作为Apache的模块
[root@apache ~]# ll /etc/httpd/modules/libphp5.so
-rwxr-xr-x. 1 root root 4588368 Jun 24 2015 /etc/httpd/modules/libphp5.so
[root@apache ~]# ll /etc/httpd/conf.d/php.conf
-rw-r--r--. 1 root root 691 Jun 24 2015 /etc/httpd/conf.d/php.conf
[root@apache ~]# systemctl restart httpd
192.168.31.154/2.php
四、安装Mariadb
[root@apache ~]# yum -y install mariadb-server mariadb
[root@apache ~]# systemctl start mariadb.service
[root@apache ~]# systemctl enable mariadb.service
[root@apache ~]# mysql_secure_installation //提升mariadb安全 [可选]
Set root password? [Y/n]
New password: 123
Re-enter new password: 123
[root@apache ~]# mysql -uroot -p123 //登录mariadb测试
MariaDB [(none)]> q
[root@apache ~]# rm -rf /var/www/html/*
[root@apache ~]# vim /var/www/html/index.php
<?php
$link=mysql_connect(‘localhost‘,‘root‘,‘123‘);
if ($link)
echo "Successfuly";
else
echo "Faile";
mysql_close();
?>
测试结果: php无法连接mysql
五、并配置php连接Mariadb
[root@apache ~]# yum -y install php-mysql
[root@apache ~]# php -m //查看php有哪些扩展
[PHP Modules]
mysql
mysqli
[root@apache ~]# systemctl restart httpd
六、Apache基本配置
[root@tianyun ~]# vim /etc/httpd/conf/httpd.conf
ServerRoot "/etc/httpd" //安装目录
Listen 80 //监听端口
IncludeOptional conf.d/.conf //包含conf.d下的.conf文件
User apache //运行Apache的用户
Group apache //运行Apache的用户组
DirectoryIndex index.html index.php //设置默认主页
DocumentRoot //站点默认主目录
<Directory "/var/www"> //Apache访问控制
AllowOverride None
Allow open access:
Require all granted
</Directory>
========================================================
配置进程和线程 针对apache2.2 仅针对面试
prefork MPM //进程模式
<IfModule prefork.c>
StartServers 10 //初始建立的进程数
MinSpareServers 10 //最小空闲的进程数
MaxSpareServers 15 //最大空闲的进程数
ServerLimit 2000 //最大启动的进程数 默认256
MaxClients 2000 //最大并发连接数 默认256
MaxRequestsPerChild 4000 //每个子进程在其生命周期内允许响应的最大请求数,0不限制
</IfModule>
worker MPM //线程模式
<IfModule worker.c>
StartServers 2 //初始建立的进程数
ThreadsPerChild 50 //每个进程建立的线程数
MinSpareThreads 100 //最小空闲的线程数
MaxSpareThreads 200 //最大空间的线程数
MaxClients 2000 //最大的并发访问量(线程)
MaxRequestsPerChild 0 //每个子进程在其生命周期内允许响应的最大请求数,0不限制
</IfModule>
========================================================
忘记MySQL密码
MySQL 5.7.5 and earlier:
[root@mysql1 ~]# vim /etc/my.cnf
[mysqld]
skip-grant-tables
[root@mysql1 ~]# service mysqld restart
[root@mysql1 ~]# mysql
mysql> update mysql.user set password=password("456") where user="root" and host="localhost";
mysql> flush privileges;
mysql> q
[root@mysql1 ~]# vim /etc/my.cnf
[mysqld]
#skip-grant-table
[root@mysql1 ~]# service mysqld restart
文章地址:
$x = 10; $y = 45; $z = 3;
//求出三个数字中最大值最小值
//先比较x y,如果x> y ,判断x 和z,从x z 中判断最大值赋给max
//如果x < y ,判断 y 和z ,从y z中判断最大值
$max = $x > $y ? ($x > $z ? $x :$z) : ($y > $z ? $y : $z);
$min = $x < $y ? ($x < $z ? $x :$z) : ($y < $z ? $y: $z);
echo $max; //45
echo ‘<br>‘;
echo $min; //3
两两计算~~求出极值
主要借助 百度开发的开源的echarts.js插件,挺好用的。(自行查看官网)
在官网实例上部分没有在对应的图形上面显示数据,所以记录下:
主要是添加label来实现在图形上显示对于的数据
饼图显示数据:
option1 = {
title: {
text: ‘学历统计‘,
subtext: ‘‘,
x: ‘center‘
},
tooltip: {
trigger: ‘item‘,
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
legend: {
orient: ‘vertical‘,
left: ‘left‘,
data: []
},
series: [
{
name: ‘‘,
type: ‘pie‘,
radius: ‘55%‘,
center: [‘50%‘, ‘60%‘],
label: {//在圆内显示具体数据数字
normal: {
formatter: ‘{b}:{d}%‘,
position: ‘inside‘
}
},
data: [],
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: ‘rgba(0, 0, 0, 0.5)‘
}
}
}
]
};
柱形图:
option = {
title: {
text: ‘人数统计‘
},
color: [‘#749f83‘],
tooltip: {},
legend: {
data: [‘人数‘]
},
xAxis: {
data: []
},
yAxis: {},
series: [{
name: ‘人数‘,
type: ‘bar‘,
label: {
normal: {
position: ‘top‘,
show: true
}
},
data: []
}]
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
// 1、封装函数 检查字符串结尾: 例如:输入confirmEnding("He has to give me a new name", "me")返回true
function confirmEnding(str, strPart) {
// 获取长度
var strLen = str.length,
strPartLen = strPart.length;
// 定义字符串从什么位置开始截取
var num = strLen - strPartLen;
// 3种方法。slice()、substring()、substr()
// if (strPart == str.slice(num)) {
// return true;
// } else {
// return false;
// }
return (strPart === str.slice(num));
}
console.log(confirmEnding("He has to give me a new name", "ffme"));
// 2、封装函数 用来反转数字 例如:输入reverseNumber(123456);返回654321
function reverseNumber(num) {
// 思路:字符串没有反转的方法,先转换为数组进行反转,然后将反转之后的数组转为字符串 split() reverse() join()
var num = num + ""; // Number转化为字符串
var arrNum = num.split("").reverse(); // 将字符串转为数组并反转
var strNum = +(arrNum.join("")); // 将数组装换为字符串并转成Number
return strNum;
}
console.log(reverseNumber(123456));
// 3、判断两边是否对称。输入isPalindrome(‘hello‘);返回false;输入isPalindrome(‘madam‘);返回true
function isPalindrome(str) {
// 奇数情况:3 5 7 9 11
// 1.5 2.5 3.5 4.5 5.5 向上取整
// 偶数情况:len/2分两边,反转一边,判断两边是否一样
var len = str.length;
var num, str1;
// 前面为偶数,后面为奇数
len % 2 == 0 ? num = len / 2 : num = Math.ceil(len / 2);
len % 2 == 0 ? str1 = str.slice(0, num).split("").reverse().join("") : str1 = str.slice(0, num - 1).split("").reverse().join("");
// 进行判断
return (str1 === str.substring(num));
}
console.log(isPalindrome("hello"), isPalindrome("madam"));
// 4、写一个函数对传入的字符串输出所有子字符串组合 例如输入substrings(‘dog‘); 返回[‘d‘, ‘do‘, ‘dog‘, ‘o‘, ‘og‘, ‘g‘]
function substrings(str) {
var arr = [];
var len = str.length
for (var i = 0; i < len; i++) {
for (var j = 1; j <= len - i; j++) {
arr.push(str.substr(i, j))
}
}
return arr
}
console.log(substrings(‘dog‘));
// 5、写一个函数对传入的字符串重新按字母排序 例如 输入reorderStr(‘webmaster‘);返回 abeemrstw
function reorderStr(str) {
return str.split("").sort().join(""); // 转换为数组,数组进行排序,然后转换为字符串
}
console.log(reorderStr(‘webmaster‘));
// 6、写一个函数对传入的字符串中每个单词的首字母大写 输入myCap(‘the quick brown fox‘); 返回The Quick Brown Fox
function myCap(str) {
var arrStr = str.split(" "); // 通过" "转换为数组
var len = arrStr.length;
// 遍历取每一项的首字母
for (var i = 0; i < len; i++) {
arrStr[i] = arrStr[i].substring(0, 1).toUpperCase() + arrStr[i].substring(1);
}
return arrStr.join(" ");
}
//
console.log(myCap(‘the quick brown fox ddd‘));
// 7、写一个函数找出传入的字符串中最长的单词 例如:输入findLongest(‘Web Development Tutorial‘); 返回 Development
function findLongest(str) {
var arrFind = str.split(" "); // 将字符串转化为数组
var len = arrFind.length;
var maxLen = ""; // 定义变量接受最大值,初始为数组的第一项
// 遍历数组中的每一项,进行长度大小对比
for (var i = 0; i < len; i++) {
if (arrFind[i].length > maxLen.length) maxLen = arrFind[i];
}
return maxLen;
}
console.log(findLongest(‘Web Development Tutorial f‘));
// 8、封装函数 实现toUpperCase方法 例如: 输入myUpperCase("hello"); 返回HEllo charCodeAt()方法
function myUpperCase(str) {
var len = str.length;
var arrStr = str.split("");
var arr = [];
for (var j = 0; j < len; j++) {
if (arrStr[j].charCodeAt() >= 97 && arrStr[j].charCodeAt() <= 122) { // 如果是小写,进入此判断
arr[j] = String.fromCharCode(arrStr[j].charCodeAt() - 32); // 将小写转换为大写
} else { // 如果不是小写
arr[j] = arrStr[j]; // 返回本身
}
}
return arr.join("");
}
console.log(myUpperCase("Hello"));
// 9、封装函数 实现数组sort的方法(参考冒泡排序)例如:输入mySort([2,33,41,10,5]); 返回 [2,5,10,33,41]
function mySort(arr) {
var len = arr.length;
for (var i = 0; i < len; i++) {
for (var j = 0; j < len - 1; j++) {
var basket; // 定义一个箩筐,方便两个值互换
if (arr[j] > arr[j + 1]) {
basket = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = basket;
}
}
}
return arr;
}
console.log(mySort([2, 33, 41, 10, 5]));
// 10、封装函数 重复输出字符串。例如:输入repeat("abc", -2); 返回abc;输入repeat("abc", 3); 返回abcabcabc;输入repeat("abc", 2); 返回abcabc
function repeat(str, num) {
var str1 = ""; // 定义变量接受遍历后的结果
// 1、判断num的值,为负或等于0时返回本身
if (num <= 0) {
return str;
} else if (num > 0) { // 为正遍历叠加他本身num遍
for (var i = 0; i <= num - 1; i++) {
str1 += str;
}
return str1;
}else{ // 其他情况返回它本身
return num;
}
}
console.log(repeat("abc", 6))
//
</script>
</body>
</html>
以上是我练习字符串方法所写,还有很多需要精进和修改的内容,欢迎指出,谢谢!!!
一、还原字体和字号
1.利用fireworks切片以及文本添加的功能可以用来定位我们想要的文字是什么字体,行高又是多少,但其实很low,是我们人工挑选出来的。
2.注意点:在企业开发中,如果一个盒子中存储的是文字,那么一般情况下我们会以盒子左边的内边距为基准,不会以右边的内边距为基准,因为这个右边的内边距会有误差(每一行的最后一个字放不下了,只能换一行,导致原来的这一行右边距反而大了一些)
二、文字界面
1.做一个网页的顺序
(1)先清空所有的边距
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{
margin:0;padding:0
}
(2)从外向内
(3)从上而下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td {
margin: 0;
padding: 0
}
div{
margin:0 auto;
box-sizing: border-box;
width:372px;
height:232px;
border:1px solid #666;/*颜色中两位是一个颜色,应该有六位,但是如果两位一样则可以省略为一位,即这一行颜色全名为#666666*/
padding:15px;
}
h1{
font-family: "微软雅黑";
font-size:18px;
border-bottom:1px solid;
padding-bottom:10px;
}/*我们用div标签设置整体的布局,用span标签来设置里面细节的部分,如下设置NEW....*/
span{
font-size:14px;/*这里面还有优先级,小标签继承了大标签的特性,同时自己设置的属性又会覆盖原来继承的属性*/
}
ul{
list-style:None;/*这个我们复习了去掉行前面的小圆点*/
margin-top:10px;
?
}
ul li {
line-height:30px;/*复习了行高*/
border-bottom:1px dashed;
padding-left:20px;
font-size:12px;
font-family:"微软雅黑";
font-color:#242424;
}
</style>
</head>
<body>
<div>
<h1>最新文章/<span>NEW Articles</span></h1>
<ul><!--这里还复习了条目的表示方法及其快捷键:ul>li*5再加tab-->
<li>优秀的国民</li>
<li>厉害的足球</li>
<li>富饶的江西</li>
<li>牛逼的赞800</li>
<li>还有无敌的我</li>
</ul>
</div>
</body>
</html>
通过上面的例子,我们一步一步演示了如何将我们的网页实现出来,这种静态网页的布局按照上面的散布进行编写。
四、源码:
d119_edit_static_webpage_of_word.html
地址:
https://github.com/ruigege66/HTML_learning/blob/master/d119_edit_static_webpage_of_word.html
2.CSDN:https://blog.csdn.net/weixin_44630050(心悦君兮君不知-睿)
3.博客园:https://www.cnblogs.com/ruigege0000/
4.欢迎关注微信公众号:傅里叶变换,个人账号,仅用于技术交流,后台回复“礼包”获取Java大数据学习视频礼包
参数必需的描述
name
是
Cookie 名称。
value
否
Cookie 值。 这个值储存于用户的电脑里,请勿储存敏感信息。 比如 name 是 ‘cookiename‘, 可通过 $_COOKIE[‘cookiename‘] 获取它的值。
expire
否
Cookie 的过期时间。 这是个 Unix 时间戳,即 Unix 纪元以来(格林威治时间 1970 年 1 月 1 日 00:00:00)的秒数。 也就是说,基本可以用 函数的结果加上希望过期的秒数。 或者也可以用 。 time()+60*60*24*30 就是设置 Cookie 30 天后过期。 如果设置成零,或者忽略参数, Cookie 会在会话结束时过期(也就是关掉浏览器时)。
path
否
Cookie 有效的服务器路径。 设置成 ‘/‘ 时,Cookie 对整个域名 domain 有效。 如果设置成 ‘/foo/‘, Cookie 仅仅对 domain 中 /foo/ 目录及其子目录有效(比如 /foo/bar/)。 默认值是设置 Cookie 时的当前目录。
domain
否
Cookie 的有效域名/子域名。 设置成子域名(例如 ‘www.example.com‘),会使 Cookie 对这个子域名和它的三级域名有效(例如 w2.www.example.com)。 要让 Cookie 对整个域名有效(包括它的全部子域名),只要设置成域名就可以了(这个例子里是 ‘example.com‘)。
secure
否
设置这个 Cookie 是否仅仅通过安全的 HTTPS 连接传给客户端。 设置成 TRUE 时,只有安全连接存在时才会设置 Cookie。 如果是在服务器端处理这个需求,程序员需要仅仅在安全连接上发送此类 Cookie (通过 $_SERVER["HTTPS"] 判断)。
httponly
否
设置成 TRUE,Cookie 仅可通过 HTTP 协议访问。 这意思就是 Cookie 无法通过类似 JavaScript 这样的脚本语言访问。 要有效减少 XSS 攻击时的身份窃取行为,可建议用此设置(虽然不是所有浏览器都支持),不过这个说法经常有争议。 PHP 5.2.0 中添加。 TRUE 或 FALSE
methods,watch和computed都是以函数为基础的,但各自却都不同;
1.watch和computed都是以Vue的依赖追踪机制为基础的,它们都试图处理这样一件事情:当某一个数据(称它为依赖数据)发生变化的时候,所有依赖这个数据的“相关”数据“自动”发生变化,也就是自动调用相关的函数去实现数据的变动。
2.对methods:methods里面是用来定义函数的,很显然,它需要手动调用才能执行。
而不像watch和computed那样,“自动执行”预先定义的函数
【总结】:methods里面定义的函数,是需要主动调用的,而和watch和computed相关的函数,会自动调用,完成我们希望完成的作用
从性质上看:
1.methods里面定义的是函数,methods是方法,只要调用它,函数就会执行;
2.computed是计算属性,事实上和和data对象里的数据属性是同一类的(使用上),
<p>原始数据:{{msg}}</p>
<p>改变后的数据:{{changemsg}}</p>
var vm=new Vue({
el:"#example",
data:{
msg:"hello",
},
computed:{ //计算属性
changemsg:function(){
return this.msg.split("").reverse().join("");
},
}
你在取用的时候,用this.changemsg去取用,就和取data一样(不要当成函数调用!!)
computed属性 VS watched 属性:
watch和computed各自处理的数据关系场景不同
1.watch擅长处理的场景:一个数据影响多个数据
2.computed擅长处理的场景:一个数据受多个数据影响
watched属性:代码更易于理解,它指定监测的值是谁,然后相应的改变其他的值。
var vm = new Vue({
el: ‘#demo‘,
data: {
firstName: ‘Foo‘,
lastName: ‘Bar‘,
fullName: ‘Foo Bar‘
},
watch: {
firstName: function (val) {
this.fullName = val + ‘ ‘ + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ‘ ‘ + val
}
}
})
computed属性:
var vm = new Vue({
el: ‘#demo‘,
data: {
firstName: ‘Foo‘,
lastName: ‘Bar‘
},
computed: {
fullName: function () {
return this.firstName + ‘ ‘ + this.lastName
}
}
})
需要注意的是,
methods调用的方法是没有缓存,
computed调用的方法是有缓存,如果之前调用过,则直接返回结果而不是重复计算
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<div>
{{ message.split(‘‘).reverse().join(‘‘) }}
</div>
<h2>计算属性</h2>
<div>
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
<button @click="change()">改变</button>
</div>
<h2>计算属性缓存 vs 方法</h2>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
<p>Reversed message: "{{ reversedMessage2() }}"</p>
<p>Reversed message: "{{ reversedMessage2() }}"</p>
</div>
<script type="text/javascript" src="https://www.gxlsystem.com/js/vue2.6.js"></script>
<script type="text/javascript">
let count=1;
let vm=new Vue({
el:‘#app‘,
data:{
message: ‘Hello‘
},
computed:{
// 计算属性的 getter
reversedMessage(){
count++;
// `this` 指向 vm 实例
return count+‘‘+this.message.split(‘‘).reverse().join(‘‘)
}
},
methods:{
change(){
this.message="fdafdafsda";
},
reversedMessage2() {
count++;
return count+‘‘+this.message.split(‘‘).reverse().join(‘‘)
}
}
})
</script>
</body>
</html>
原文出处:
https://my.oschina.net/u/3649083/blog/1560106
http://blog.java1234.com/blog/articles/533.html
一、封装Ajax
1 <script>
2 var ajax = new Promise((resolve, reject) => {
3 var xhr = new XMLHttpRequest();
4 xhr.open(‘get‘, ‘https://www.gxlsystem.com/test1.txt‘, true);
5 xhr.send();
6 xhr.onreadystatechange = function() {
7 if (xhr.status == 200 && xhr.readyState == 4) {
8 resolve(xhr.responseText);
9 }
10 setTimeout(function() {
11 reject(new Error(‘连接超时‘));
12 }, 2000);
13 }
14 });
15
16 ajax.then(function(data) {
17 console.log(data);
18 }).catch(function(err) {
19 console.error(err);
20 })
21 </script>
二、封装get请求
1 <script>
2 function $get(url, data) {
3 if (typeof data === ‘object‘) {
4 url += "?time=" + Date.now();
5 for (var i in data) {
6 url += "&" + i + "=" + data[i];
7 }
8 }
9 return new Promise((resolve, reject) => {
10 var xhr = new XMLHttpRequest();
11 xhr.open(‘get‘, url, true);
12 xhr.send();
13 xhr.onreadystatechange = function() {
14 if (xhr.readyState == 4 && xhr.status == 200) {
15 resolve(xhr.responseText);
16 }
17 setTimeout(() => {
18 reject(new Error(‘连接超时‘));
19 }, 2000);
20 }
21 });
22 }
23
24 $get(‘https://www.gxlsystem.com/test.txt‘, {
25 "username": "zhansgan"
26 }).then(function(data) {
27 console.log(data);
28 }).catch(function(err) {
29 console.error(err);
30 })
31 </script>
在nginx中使用json格式输出日志记录
log_format json ‘{ "time_local": "$time_local", ‘‘"remote_addr": "$remote_addr", ‘‘"referer": "$http_referer", ‘‘"request": "$request", ‘‘"status": $status, ‘‘"bytes": $body_bytes_sent, ‘‘"agent": "$http_user_agent", ‘‘"x_forwarded": "$http_x_forwarded_for", ‘‘"up_addr": "$upstream_addr",‘‘"up_host": "$upstream_http_host",‘‘"upstream_time": "$upstream_response_time",‘‘"request_time": "$request_time"‘‘ }‘;
access_log /var/log/nginx/access.log json;
对于使用filebeat来收集日志很重要。
方法说明
方法
说明
Restrictions.eq
=
Restrictions.allEq
利用Map来进行多个等于的限制
Restrictions.gt
>
Restrictions.ge
>=
Restrictions.lt
<
Restrictions.le
<=
Restrictions.between
BETWEEN
Restrictions.like
LIKE
Restrictions.in
in
Restrictions.and
and
Restrictions.or
or
Restrictions.sqlRestriction
用SQL限定查询
2,QBC常用限定方法
Restrictions.eq --> equal,等于.
Restrictions.allEq --> 参数为Map对象,使用key/value进行多个等于的比对,相当于多个Restrictions.eq的效果
Restrictions.gt --> great-than > 大于
Restrictions.ge --> great-equal >= 大于等于
Restrictions.lt --> less-than, < 小于
Restrictions.le --> less-equal <= 小于等于
Restrictions.between --> 对应SQL的between子句
Restrictions.like --> 对应SQL的LIKE子句
Restrictions.in --> 对应SQL的in子句
Restrictions.and --> and 关系
Restrictions.or --> or 关系
Restrictions.isNull --> 判断属性是否为空,为空则返回true
Restrictions.isNotNull --> 与isNull相反
Restrictions.sqlRestriction --> SQL限定的查询
Order.asc --> 根据传入的字段进行升序排序
Order.desc --> 根据传入的字段进行降序排序
MatchMode.EXACT --> 字符串精确匹配.相当于"like ‘value‘"
MatchMode.ANYWHERE --> 字符串在中间匹配.相当于"like ‘%value%‘"
MatchMode.START --> 字符串在最前面的位置.相当于"like ‘value%‘"
MatchMode.END --> 字符串在最后面的位置.相当于"like ‘%value‘"
例子
查询年龄在20-30岁之间的所有学生对象
List list = session.createCriteria(Student.class)
.add(Restrictions.between("age",new Integer(20),new Integer(30)).list();
查询学生姓名在AAA,BBB,CCC之间的学生对象
String[] names = {"AAA","BBB","CCC"};
List list = session.createCriteria(Student.class)
.add(Restrictions.in("name",names)).list();
查询年龄为空的学生对象
List list = session.createCriteria(Student.class)
.add(Restrictions.isNull("age")).list();
查询年龄等于20或者年龄为空的学生对象
List list = session.createCriteria(Student.class)
.add(Restrictions.or(Restrictions.eq("age",new Integer(20)),
Restrictions.isNull("age")).list();
--------------------------------------------------------------------
使用QBC实现动态查询
public List findStudents(String name,int age){
Criteria criteria = session.createCriteria(Student.class);
if(name != null){
criteria.add(Restrictions.liek("name",name,MatchMode.ANYWHERE));
}
if(age != 0){
criteria.add(Restrictions.eq("age",new Integer(age)));
}
criteria.addOrder(Order.asc("name"));//根据名字升序排列
return criteria.list();
}
-----------------------------------------------------------------------------------
今天用了写hibernate高级查询时用了Restrictions(当然Expression也是可以以的)这个类.感觉不错.
下面的代码写的不易读.其实核心就是一句
Restrictions.or(Restrictions.like(),Restrictions.or(Restrictions.like,........))
里面的or可以无限加的.还是比较好用
Session session = getHibernateTemplate().getSessionFactory()
.openSession();
Criteria criteria = session.createCriteria(Film.class);
List<Film> list = criteria.add(
Restrictions.or(Restrictions.like("description", key,MatchMode.ANYWHERE),
Restrictions.or(Restrictions.like("name", key,MatchMode.ANYWHERE),
Restrictions.or( Restrictions.like("direct", key,MatchMode.ANYWHERE),
Restrictions.or(Restrictions.like("mainplay",key,MatchMode.ANYWHERE),
Restrictions.like("filearea", key,MatchMode.ANYWHERE)))))).list();
session.close();
return list;
注明出处:https://www.cnblogs.com/langtianya/p/4718216.html
springmvc框架
1.1 什么是springmvc
springmvc是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合。
springmvc是一个基于mvc的web框架。
1.2 mvc在b/s系统 下的应用
mvc是一个设计模式,mvc在b/s系统 下的应用:
1.3 springmvc框架
第一步:发起请求到前端控制器(DispatcherServlet)
第二步:前端控制器请求HandlerMapping查找 Handler,可以根据xml配置、注解进行查找
第三步:处理器映射器HandlerMapping向前端控制器返回Handler
第四步:前端控制器调用处理器适配器去执行Handler
第五步:处理器适配器去执行Handler
第六步:Handler执行完成给适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回ModelAndView,ModelAndView是springmvc框架的一个底层对象,包括 Model和view
第八步:前端控制器请求视图解析器去进行视图解析,根据逻辑视图名解析成真正的视图(jsp)
第九步:视图解析器向前端控制器返回View
第十步:前端控制器进行视图渲染,视图渲染将模型数据(在ModelAndView对象中)填充到request域
第十一步:前端控制器向用户响应结果
组件:
1、前端控制器DispatcherServlet(不需要程序员开发) 作用接收请求,响应结果,相当于转发器,中央处理器。
有了DispatcherServlet减少了其它组件之间的耦合度。
2、处理器映射器HandlerMapping(不需要程序员开发) 作用:根据请求的url查找Handler
3、处理器适配器HandlerAdapter 作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler
4、处理器Handler(需要程序员开发) 注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler
5、视图解析器View resolver(不需要程序员开发) 作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)
6、视图View(需要程序员开发jsp) View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)
来自:https://github.com/ElemeFE/element/issues/3367#issuecomment-376402380 侵删
el-upload里面绑定一个占位class:
data里面初始值:
onChange里面(添加文件、上传成功和上传失败时都会被调用的那个):
handleRemove里面(删除文件被调用的那个):
style,把scoped去掉(或者外部引入样式文件,主要目的是为了修改element-ui自带的样式)
首先说一下这个功能的写法没有错,但是我正在找是哪个地发出错了,这个功能我没有实现,先记录一下( 报我类型的错 )
结构
<el-upload
:action="actionsUrl" //上传图片的路径
:show-file-list="false"
:on-success="handleAvatarSuccess" //上传成功的回调
name="fileName" //name值必须有
:limit="1" //限制上传数量
>
<img
v-if="this.ruleForm.img"
:src="this.ruleForm.img"
class="avatar"
/> //图片的位置
<i v-else class="el-icon-plus avatar-uploader-icon"></i> //小图标
</el-upload>
data
ruleForm:{
img:"",//图片的绑定需要用它赋值地址
actionsUrl: "/api" + "/grouplesson/edit" //地址最好是拼接一下
}
事件
handleAvatarSuccess(res, file) {
//这里是如果成功了参数中有相关信息关于地址的然后在赋值就可以
//然后成功后在把this.ruleForm.img传给后端
console.log(res);
this.ruleForm.img = res.相关地址
},
css
.avatar-uploader /deep/.el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 95px;
height: 95px;
line-height: 95px;
text-align: center;
border: 1px dashed #d9d9d9;
border-radius: 6px;
}
.avatar {
width: 95px;
height: 95px;
}
现在我们已经实现了我们想要实现的功能,但是这些功能还不完美。如果是细心的读者,在看上一篇文章()时会发现一些我们需要的复选框并没有出现在我们想要它出现的位置。如下图中红框处,我们想要出现一个复选框,但实现上却没有出现。下面我们来解决这个问题。
十四、样式冲突
1. 首先,我们在Visual Studio 2017中按F5运行应用程序。
2.在浏览器中的地址栏中输入“http://localhost:5000/”,然后输入管理员用户名进行登录。
3.在主界面的菜单中,选择“Business->货物管理”菜单项,浏览器中呈现一个货物信息列表与四个按钮。如下图。你会发现这个列表有问题。如下图红框处,没有复选框。
4.我们在浏览器(Firefox)在复选框的位置,使用单击鼠标右键,在弹出菜单中选中“查看元素”,如下图。然后我们看到在html代码中是有复选框的相关代码的,但是却没有在页面中显示。我们来看一下这个复选框的样式,如下图中的红框处,原来checkbox元素的样式表冲突了。此处的Checkbox元素的样式并不是我们想要的是EasyUI.css中的样式定义,而实际上却使用了materialize.css样式表中的样式定义。造成了这个Checkbox元素左移了-9999px像素,变的不可见了。
5. 知道了问题原因,我们可以直接在Firefox调试器中修改一下样式,把样式中的left的值由-9999px修改为1px,opacity的值由0改为2,如下图红框处,然后我们需要的复选框就出现在我们想要的位置。如下图。
6. 那应该如何来解决这个问题呢?仔细观察调试器中的HTML代码,发现Checkbox外面还有一层DIV,这行DIV的class名称为“datagrid-cell-check”。这个发现,我们就有办法解决这个样式冲突问题了。在Visual Studio 2017的“解决方案资源管理器”中,找到“ABP.TPLMS.Web.MVC”项目的“wwwrootlibeasyui-1.8 hemesbootstrap”文件夹中找到easyui.css文件,在这个文件中添加如下样式。代码如下。
.datagrid-header-check input[type=checkbox],
.datagrid-cell-check input[type=checkbox] {
position: absolute;
left: 5px;
opacity: 1;
margin: 0;
padding: 0;
width: 15px;
height: 18px;
}
7.在浏览器中先访问一下其他页面,然后再来访问货物信息列表,此时列表的复选框出现了。如下图。
1
kubectl run hello --schedule="*/1 * * * *" --restart=OnFailure --image=busybox -- /bin/sh -c "date; echo Hello from the Kubernetes cluster"
一、jquery video插件库
Html5 Video是现在html5最流行的功能之一,得到了大多数最新版本的浏览器支持.包括IE9,也是如此.不同的浏览器提供了不同的原生态浏览器视频空间.我们制作自定义视频控件为了在所有的浏览器中有一个相同的Html5视频控件而不受默认视频控件的控制.
实际上,自定义视频控件并不困难.本文将告诉你如何用jQuery自定义视频控件,希望对你有用!
HTML5 基础标签
<video id="myVideo" controls poster="video.jpg" width="600" height="400" >
<source src="https://www.gxlsystem.com/video.mp4" type="video/mp4" />
<source src="https://www.gxlsystem.com/video.webm" type="video/webM" />
<source src="https://www.gxlsystem.com/video.ogv" type="video/ogg" />
<p>Your browser does not support the video tag.</p>
</video>
幸运的是HTML5 Video 的Api可以用JavaScript访问,并使用他们来作为控制视频的媒介.
在编码之前让我简单的介绍一下jQuery是如何获取video标签的.
在JavaScript中我们使用getElementById(‘videoID‘)来获取Video标签,作为结果,我们会获取到一个Dom对象.但是这不是等价的jQuery对象.$("videoID")会返回一个jQuery对象.不是Dom对象.这就是为什么在将其转换为Dom对象之前我们不能直接使用jQuery选择器调用/使用Html5 Video的Dom属性和功能.
//return a DOM object
var video = document.getElementById(‘videoID‘); //or
var video = $(‘#videoID‘).get(0); //or
var video = $(‘#videoID‘)[0];
//return a jQuery object
var video = $(‘#videoID‘);
Video Play/Pause Controls 播放/暂停 按钮
好的,这是所有的介绍.现在让我们来编码.首先,我们要创建一个简单的播放/暂停按钮.
<div class="control">
<a href="https://www.gxlsystem.com#" class="btnPlay">Play/Pause</a>
</div>
我们可以轻松的控制Html5 Video的播放与暂停状态.
//Play/Pause control clicked
$(‘.btnPlay‘).on(‘click‘, function() {
if(video[0].paused) {
video[0].play();
}
else {
video[0].pause();
}
return false;
};
显示视频播放时间和持续时间
Html5 Video支持视频回放.这里我们要显示视频的当前播放时间和总时间.
<div class="progressTime">
Current play time: <span class="current"></span>
Video duration: <span class="duration"></span>
</div>
为了得到视频的总时间,我们要确保视频元数据已经加载.这个时候我们要用到Html5 Video的loadedmetadata事件.
对于当前的视频播放时间.我们可以用Html5 Video timeupdate事件来保证他的更新.
//get HTML5 video time duration
video.on(‘loadedmetadata‘, function() {
$(‘.duration‘).text(video[0].duration);
});
//update HTML5 video current play time
video.on(‘timeupdate‘, function() {
$(‘.current‘).text(video[0].currentTime);
});
视频进度条
在这里我们将会把当前播放时间和总的时间长度转换为更人性化的进度条.
<style>
.progressBar
{
position: relative;
width: 100%;
height: height:10px;
backgroud-color: #000;
}
.timeBar
{
position: absolute;
top: 0;
left: 0;
width: 0;
height: 100%;
background-color: #ccc;
}
</style>
<div class="progressBar">
<div class="timeBar"></div>
</div>
下面的js就是通过视频的总时间与当前时间的计算,获得播放进度条。
//get HTML5 video time duration
video.on(‘loadedmetadata‘, function() {
$(‘.duration‘).text(video[0].duration));
});
//update HTML5 video current play time
video.on(‘timeupdate‘, function() {
var currentPos = video[0].currentTime; //Get currenttime
var maxduration = video[0].duration; //Get video duration
var percentage = 100 * currentPos / maxduration; //in %
$(‘.timeBar‘).css(‘width‘, percentage+‘%‘);
});
下面实现播放进度条的拖拽,来播放视频
var timeDrag = false; /* Drag status */
$(‘.progressBar‘).mousedown(function(e) {
timeDrag = true;
updatebar(e.pageX);
});
$(document).mouseup(function(e) {
if(timeDrag) {
timeDrag = false;
updatebar(e.pageX);
}
});
$(document).mousemove(function(e) {
if(timeDrag) {
updatebar(e.pageX);
}
});
//update Progress Bar control
var updatebar = function(x) {
var progress = $(‘.progressBar‘);
var maxduration = video[0].duration; //Video duraiton
var position = x - progress.offset().left; //Click pos
var percentage = 100 * position / progress.width();
//Check within range
if(percentage > 100) {
percentage = 100;
}
if(percentage < 0) {
percentage = 0;
}
//Update progress bar and video currenttime
$(‘.timeBar‘).css(‘width‘, percentage+‘%‘);
video[0].currentTime = maxduration * percentage / 100;
};
进阶-显示缓冲栏
我们需要给视频制作一个缓冲栏让用户知道视频加载了多少.
<style>
.progressBar {
position: relative;
width: 100%;
height: height:10px;
backgroud-color: #000;
}
.bufferBar {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 100%;
background-color: #ccc;
}
</style>
<div class="progressBar">
<div class="bufferBar"></div>
</div>
Html5 Video缓冲属性将返回一个对象的缓存范围.因此,我们将使用缓存数据的最后一个值.
//loop to get HTML5 video buffered data
var startBuffer = function() {
var maxduration = video[0].duration;
var currentBuffer = video[0].buffered.end(0);
var percentage = 100 * currentBuffer / maxduration;
$(‘.bufferBar‘).css(‘width‘, percentage+‘%‘);
if(currentBuffer < maxduration) {
setTimeout(startBuffer, 500);
}
};
setTimeout(startBuffer, 500);
音量控制
现在,我们要增加声音控制.有两种不同的音量控制方法.静音按钮/音量栏
<a href="https://www.gxlsystem.com#" class="muted" >Mute/Unmute</a>
<div class="volumeBar">
<div class="volume"></div>
</div>
js:
//Mute/Unmute control clicked
$(‘.muted‘).click(function() {
video[0].muted = !video[0].muted;
return false;
});
//Volume control clicked
$(‘.volumeBar‘).on(‘mousedown‘, function(e) {
var position = e.pageX - volume.offset().left;
var percentage = 100 * position / volume.width();
$(‘.volumeBar‘).css(‘width‘, percentage+‘%‘);
video[0].volume = percentage / 100;
});
快进/快退 倒带控制
Html5 Video支持播放速度的改变.我们可以使用playbackrate属性来控制.
<div class="control">
<a href="https://www.gxlsystem.com#" class="ff">Fast Forward</a>
<a href="https://www.gxlsystem.com#" class="rw">Rewind</a>
<a href="https://www.gxlsystem.com#" class="sl">Slow Motion</a>
</div>
不幸的是FireFox不支持playbackrate属性.以及有些版本的chrome浏览器不支持负值(倒带).到目前为止,只有Safri浏览器完全支持.
/Fast forward control
$(‘.ff‘).on(‘click‘, function() {
video[0].playbackrate = 3;
return false;
});
//Rewind control
$(‘.rw‘).on(‘click‘, function() {
video[0].playbackrate = -3;
return false;
});
//Slow motion control
$(‘.sl‘).on(‘click‘, function() {
video[0].playbackrate = 0.5;
return false;
});
其他
除了主要的控制插件.还可以做一些额外的控制.例如全屏播放
$(‘.fullscreen‘).on(‘click‘, function() {
//For Webkit
video[0].webkitEnterFullscreen();
//For Firefox
video[0].mozRequestFullScreen();
return false;
});
开灯关灯控制
$(‘.btnLight‘).click(function() {
if($(this).hasClass(‘on‘)) {
$(this).removeClass(‘on‘);
$(‘body‘).append(‘<div class="overlay"></div>‘);
$(‘.overlay‘).css({
‘position‘:‘absolute‘,
‘width‘:100+‘%‘,
‘height‘:$(document).height(),
‘background‘:‘#000‘,
‘opacity‘:0.9,
‘top‘:0,
‘left‘:0,
‘z-index‘:999
});
$(‘#myVideo‘).css({
‘z-index‘:1000
});
}
else {
$(this).addClass(‘on‘);
$(‘.overlay‘).remove();
}
return false;
});
标题:从零开始实现ASP.NET Core MVC的插件式开发(六) - 如何加载插件引用。
作者:Lamond Lu
地址:
源代码:
前景回顾
简介
在前一篇中,我给大家演示了如何使用.NET Core 3.0中新引入的来实现运行时升级和删除插件。完成此篇之后,我得到了很多园友的反馈,很高兴有这么多人能够参与进来,我会根据大家的反馈,来完善这个项目。本篇呢,我将主要解决加载插件引用的问题,这个也是反馈中被问的最多的问题。
问题用例
在之前做的插件中,我们做的都是非常非常简单的功能,没有引入任何的第三方库。但是正常情况下,我们所创建的插件或多或少的都会引用一些第三方库,那么下面我们来尝试一下,使用我们先前的项目,加载一个使用第三方程序集, 看看会的得到什么结果。
这里为了模拟,我创建了一个新的类库项目, 并在之前的项目中引用项目。
在中,我新建了一个类Demo.cs文件, 其代码如下:
这里就是简单的通过方法,返回了一个字符串。
然后在项目中,我们修改之前创建的,从类中通过方法得到需要在页面中显示的字符串。
最后我们打包一下插件,重新将其安装到系统中,访问插件路由之后,就会得到以下错误。
这里就是大部分同学遇到的问题,无法加载程序集。
如何加载插件引用?
这个问题的原因很简单,就是当通过加载程序集的时候,我们只加载了插件程序集,没有加载它引用的程序集。
例如,我们以的为例,在这个插件的目录如下
在这个目录中,除了我们熟知的,之外,还有一个文件。 这个文件我们并没有在插件启用时加载到当前的中,所以在访问插件路由时,系统找不到这个组件的dll文件。
为什么、、这些DLL不会出现问题呢?
在.NET Core中有2种。 一种是我们之前介绍的, 它是一种自定义。 另外一种就是系统默认的。当一个.NET Core应用启动的时候,都会创建并引用一个。
如果没有指定, 系统默认会将程序集都加载到中。这里我们可以查看一下我们的主站点项目,这个项目我们也引用了、、。
在.NET Core的设计文档中,对于程序集加载有这样一段描述
If the assembly was already present in A1‘s context, either because we had successfully loaded it earlier, or because we failed to load it for some reason, we return the corresponding status (and assembly reference for the success case).
However, if C1 was not found in A1‘s context, the Load method override in A1‘s context is invoked.
For Custom LoadContext, this override is an opportunity to load an assembly before the fallback (see below) to Default LoadContext is attempted to resolve the load.
For Default LoadContext, this override always returns null since Default Context cannot override itself.
这里简单来说,意思就是当在一个自定义中加载程序集的时候,如果找不到这个程序集,程序会自动去默认中查找,如果默认中都找不到,就会返回。
由此,我们之前的疑问就解决了,这里正是因为主站点已经加载了所需的程序集,虽然在插件的中找不到这个程序集,程序依然可以通过默认来加载程序集。
那么是不是真的就没有问题了呢?
其实我不是很推荐用以上的方式来加载第三方程序集。主要原因有两点
不同插件可以引用不同版本的第三方程序集,可能不同版本的第三方程序集实现不同。 而默认只能加载一个版本,导致总有一个插件引用该程序集的功能失效。
默认中可能加载的第三方程序集与其他插件都不同,导致其他插件功能引用该程序集的功能失效。
所以这里最正确的方式,还是放弃使用默认加载程序集,保证每个插件的都完全加载所需的程序集。
那么如何加载这些第三方程序集呢?我们下面就来介绍两种方式
原始方式
使用插件缓存
原始方式
原始方式比较暴力,我们可以选择加载插件程序集的同时,加载程序集所在目录中所有的dll文件。
这里首先我们创建了一个插件引用库加载器接口。
然后我们创建一个默认的插件引用库加载器,其代码如下:
代码解释
这里我是为了排除当前已经加载插件程序集,所以添加了一个参数。
即当前插件的所在目录,这里我们通过类的方法,获取了当前指定目录中的所有dll文件。
这里我依然通过文件流的方式加载了插件所需的第三方程序集。
完成以上代码之后,我们还需要修改启用插件的两部分代码
[MystiqueStartup.cs] - 程序启动时,注入服务,启用插件
[MvcModuleSetup.cs] - 在插件管理页面,触发启用插件操作
MystiqueStartup.cs
MvcModuleSetup.cs
现在我们重新运行之前的项目,并访问插件1的路由,你会发现页面正常显示了,并且页面内容也是从程序集中加载出来了。
使用插件缓存
原始方式虽然可以帮助我们成功加载插件引用程序集,但是它并不效率,如果插件1和插件2引用了相同的程序集,当插件1的加载所有的引用程序集之后,插件2会将插件1所干的事情重复一遍。这并不是我们想要的,我们希望如果多个插件同时使用了相同的程序集,就不需要重复读取dll文件了。
如何避免重复读取dll文件呢?这里我们可以使用一个静态字典来缓存文件流信息,从而避免重复读取dll文件。
如果大家觉着在ASP.NET Core MVC中使用静态字典来缓存文件流信息不安全,可以改用其他缓存方式,这里只是为了简单演示。
这里我们首先创建一个引用程序集缓存容器接口, 其代码如下:
代码解释
方法会在后续使用,用来获取系统中加载的所有引用程序集
方法判断了指定版本程序集的文件流是否存在
是将指定版本的程序集文件流保存到静态字典中
是从静态字典中拉取指定版本程序集的文件流
然后我们可以创建一个引用程序集缓存容器的默认实现类,其代码如下:
这个类比较简单,我就不做太多解释了。
完成了引用缓存容器之后,我修改了之前创建的接口,及其默认实现。
代码解释:
这里方法的参数,即当前插件程序集。
这里我通过方法,获取了插件程序集引用的所有程序集。
如果引用程序集在引用容器中不存在,我们就是用文件流加载它,并将其保存到引用容器中, 如果引用程序集已存在于引用容器,就直接加载到当前插件的中。这里为了检验效果,如果程序集来自缓存,我使用日志组件输出了一条日志。
由于插件引用的程序集,有可能是来自, 这种程序集是不需要加载的,所以这里我选择跳过这类程序集的加载。(这里我还没有考虑Self-Contained发布的情况,后续这里可能会更改)
最后我们还是需要修改和中启用插件的代码。
MystiqueStartup.cs
MvcModuleSetup.cs
完成代码之后,为了检验效果,我创建了另外一个插件, 这个项目的代码和基本一样。程序启动时,你会发现所使用的引用程序集都是从缓存中加载的,而且的路由也能正常访问。
添加页面来显示加载的第三方程序集
这里为了显示一下系统中加载了哪些程序集,我添加了一个新页面, 这个页面就是调用了接口中定义的方法,显示了静态字典中,所有加载的程序集。
效果如下:
几个测试场景
最后,在编写完成以上代码功能之后,我们使用以下几种场景来测试一下,看一看为我们提供的强大功能。
场景1
2个插件,一个引用的1.0.0.0版本,另外一个引用的1.0.1.0版本。其中1.0.0.0版本,方法返回的字符串是"Hello World. Version 1", 1.0.1.0版本, 方法返回的字符串是“Hello World. Version 2”。
启动项目,安装插件1和插件2,分别运行插件1和插件2的路由,你会得到不同的结果。这说明为我们做了很好的隔离,插件1和插件2虽然引用了相同插件的不同版本,但是互相之间完全没有影响。
场景2
当2个插件使用了相同的第三方库,并加载完成之后,禁用插件1。虽然他们引用的程序集相同,但是你会发现插件2还是能够正常访问,这说明插件1的的释放,对插件2的完全没有影响。
总结
本篇我为大家介绍了如何解决插件引用程序集的加载问题,这里我们讲解了两种方式,原始方式和缓存方式。这两种方式的最终效果虽然相同,但是缓存方式的效率明显更高。后续我会根据反馈,继续添加新内容,大家敬请期待。
自定义实现angular中数据的状态管理,如有不妥请指正
一、先介绍一下rxjs中subject;
Import {subject}from’rxjs’
Subject 数据的订阅与分发,结合报刊的发布与订阅进行功能的模拟,subject即是observeable对象也是observer对象,subject对于后期没有数据更新时所添加的订阅者是不怎么友好的,因为不跟新数据时订阅者就不在收到返回的数值
const interval$ = interval(1000).pipe(take(10));
const subject = new Subject();
const observerA = {
next: value => console.log(‘Observer A get value: ‘ + value),
error: error => console.log(‘Observer A error: ‘ + error),
complete: () => console.log(‘Observer A complete!‘),
};
const observerB = {
next: value => console.log(‘Observer B get value: ‘ + value),
error: error => console.log(‘Observer B error: ‘ + error),
complete: () => console.log(‘Observer B complete!‘),
};
subject.subscribe(observerA); // 添加观察者A
interval$.subscribe(subject); // 订阅interval$对象
setTimeout(() => {
subject.subscribe(observerB); // 添加观察者B
}, 1000);
Import{BehaviorSubject}from’rxjs’;
behaviorSubject 是subject的变种,最大的区别就是 behaviorSubject是用于保存最新的数值,而不是单纯的发送事件,会将最后一次发送的值作为当前值保存在内部属性中。
const subject = new BehaviorSubject(0); //BehaviorSubject小括号0代表的是状态
const observerA = {
next: value => console.log(‘Observer A get value: ‘ + value),
error: error => console.log(‘Observer A error: ‘ + error),
complete: () => console.log(‘Observer A complete!‘),
};
const observerB = {
next: value => console.log(‘Observer B get value: ‘ + value),
error: error => console.log(‘Observer B error: ‘ + error),
complete: () => console.log(‘Observer B complete!‘),
};
subject.subscribe(observerA); // 添加观察者A
// interval$.subscribe(subject); // 订阅interval$对象
subject.next(1);
subject.next(2);
subject.next(3);
setTimeout(() => {
subject.subscribe(observerB); // 添加观察者B
}, 1000);
Import {ReplaySubject}from’rxjs’;
ReplaySubject 用于重复发送最近几次的值给订阅者
const subject = new ReplaySubject(2); //ReplaySubject后的2为最后两次发送的数值
const observerA = {
next: value => console.log(‘Observer A get value: ‘ + value),
error: error => console.log(‘Observer A error: ‘ + error),
complete: () => console.log(‘Observer A complete!‘),
};
const observerB = {
next: value => console.log(‘Observer B get value: ‘ + value),
error: error => console.log(‘Observer B error: ‘ + error),
complete: () => console.log(‘Observer B complete!‘),
};
subject.subscribe(observerA); // 添加观察者A
// interval$.subscribe(subject); // 订阅interval$对象
subject.next(1);
subject.next(2);
subject.next(3);
setTimeout(() => {
subject.subscribe(observerB); // 添加观察者B
}, 1000);
Import{AsyncSubject}from’rxjs’;
AsyncSubject他会在subject完成后才返回一个值
const subject = new AsyncSubject();
const observerA = {
next: value => console.log(‘Observer A get value: ‘ + value),
error: error => console.log(‘Observer A error: ‘ + error),
complete: () => console.log(‘Observer A complete!‘),
};
const observerB = {
next: value => console.log(‘Observer B get value: ‘ + value),
error: error => console.log(‘Observer B error: ‘ + error),
complete: () => console.log(‘Observer B complete!‘),
};
subject.subscribe(observerA); // 添加观察者A
// interval$.subscribe(subject); // 订阅interval$对象
subject.next(1);
subject.next(2);
subject.next(3);
subject.complete();
setTimeout(() => {
subject.subscribe(observerB); // 添加观察者B
}, 1000);
我们要实现angular的全局数据管理就需要用到 《BehaviorSubject》
二、angular服务文件
在app.module.ts中注册服务文件
import { SomeSharedService } from ‘@shared/window-service/window.service‘;
providers: [
...
SomeSharedService,
],
TS文件:service.module.ts
import { NgModule, ModuleWithProviders } from ‘@angular/core‘;
import { SomeSharedService } from ‘https://www.gxlsystem.com/window.service‘;
export { SomeSharedService };
@NgModule()
export class ServicesModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: ServicesModule,
providers: [SomeSharedService],
};
}
}
TS服务文件名:window.service.ts
import { Injectable } from ‘@angular/core‘;
import { BehaviorSubject } from ‘rxjs‘;
@Injectable()
export class SomeSharedService {
public globalVar: BehaviorSubject<any> = new BehaviorSubject({
dataCount1: 0,
dataCount2: 0,
dataCount3: 0,
dataSum: 0,
});
settingKey(key, sumKey) {
const globalVar = this.globalVar.getValue();
globalVar[key] -= 1;
globalVar[sumKey] -= 1;
this.globalVar.next(globalVar);
}
}
三、全局数据初始化
在全局公用组件中进行全局数据的初始化,具体怎么用看自己怎么考虑,页面刷新时数据都会重新向后台拿取数据;
ngOnInit(): void {
const source = timer(0, 30000);
const data = source.pipe(
mergeMap(val => {
return this.http.get(‘/admin‘);
}),
distinctUntilChanged(),
);
this.distinctSub = data.subscribe(res => {
this.someSharedService$.globalVar.next(res.data);
});
}
ngOnDestroy(): void {
this.distinctSub.unsubscribe();
}
因为业务需要 定时向后台请求一次数据更新,所以简单写了一下 ,如果不需要就只要放一个http请求就行了;
使用 this.someSharedService$.globalVar.next(res.data); 从全局服务SomeSharedService文件中分发文件;
四、订阅服务数据
在需要的页面订阅分发内容,且会保存最后一次的数据;
import { SomeSharedService } from ‘@shared/window-service/window.service‘;
constructor(
private someSharedService$: SomeSharedService,
) {}
...
this.someSharedService.globalVar.subscribe(res => {
if (!(this.cdr as ViewRef).destroyed) {
this.item = res;
this.cdr.detectChanges();
}
});
因为有一些数据渲染的问题 所以需要加一层判断,这就基本实现了从后台拿取数据,在多个页面进行展示;
五、实现数据修改及同步更新
import { SomeSharedService } from ‘@shared/window-service/window.service‘;
constructor(
private someSharedService$: SomeSharedService,
) {}
...
.subscribe(res => {
if (res.code !== 200) {
this.msg.error(res.message);
return;
}
this.someSharedService$.settingKey(‘dataCount1‘, ‘dataSum‘);
})
当完成数据请求后,调用我们内部方法,就可以在本地同步实现更新数据了;
其中原理将在后期有空时更新。
subject
1、首先我们先了解一下什么是前端,简单说,就是我们平常所浏览的网页啊,以及app首页和小程序。
2、我们经常访问浏览器的时候,经常会输入网址,又可称为域名http://www...com.有时候是http有时候又是https,二者的区别在于后者更加安全些,有秘钥。www就是万维网,后缀com、org、edu、cn以及DNS称为域名。
3、web开发设计的基础之一是html。html基本框架有以下几部分组成:结构<html><!DOCTYPE html> https://www.gxlsystem.com//声明网页类型 lang="en" // 语言 en ch<meta charset="UTF-8"> //信息标签 charset="UTF-8" 网页的编码类型 utf-8<head><title>标题</title></head><body>内容</body></html>以上标签都是带结尾的闭合,当然也有自闭和。
3、了解过html的结构,下面我们配置开发环境,我所用的软件是JetBrains WebStorm10.这个软件是要钱的,然后登录后首先进入File-setting-font-color 或者alt+ctrl+s设置字体大小和颜色。然后在file-new-dicrectory新建一个目录,然后在这目录下建html5文件 即file-new-html5 file.
4、创建完一个文件后,了解基本用法和相关标签alt+F2 浏览或右上角也可快速浏览按alt键+鼠标单击,可以在不同地方输入相同文字在每一行的末尾ctrl+D 进行行复制输入lorem按tab键,可以添加测试文本p*数字 按tab键,可以输入多行标签P
5、建立一个网页在body下编辑内容,设计网页常用标签
a)超链接<a href=https://www.gxlsystem.com/"链接的目标地址" target="打开目标窗口"></a>target: _blank 在新窗口打开_parent 在源窗口打开锚记:
1、跳转到同一个页面的不同位置在跳转位置处添加一个锚记,<a name="名称"></a>创建链接 <a href=https://www.gxlsystem.com/“#锚记名称”>点击内容</a>
2、跳转到不同页面的不同位置在跳转位置处添加一个锚记,<a name="名称"></a>创建链接 <a href=https://www.gxlsystem.com/“文件名#锚记名称”>点击内容</a>
b)发送邮件:mailto:邮箱地址c)路径:绝对路径 :从根目录开始相对路径 :从当前目录开始d)常用标签水平线 <hr width="宽度" color="颜色" size="线粗细"/>html注释 <!--注释文本--> ctrl+shift+/ 换行 <br/>空格: 全角状态下空格标题 h1~h6<pre></pre> 预格式化<em></em>斜体 <i></i><b></b>加粗 <strong></strong> <big></big>字体放大 <small></small><code></code>放置代码<del></del>删除线<sub></sub>下标<sup></sup>上标单位: 像素( px) 百分比(%) em rem
图片插入所用标签: <img src=https://www.gxlsystem.com/"图片路径" alt="信息提示" title=“图片描述” width=“图片宽度”height=“图片高度”/>
e)表格<table border="边框线" cellspacing="边框单元格间距" cellpadding="单元格填充" width="表格宽度"><caption>表头</caption><tr><th>标题行</th></tr><tr align="对方方式"><td>单元格</td></tr></table>跨行跨列rowspan colspan
f)列表无序列表<ul type=""><li></li></ul>type:disc(默认实心圆) square 方块 circle 空心圆有序列表<ol type="" start=""><li></li></ol>type:1 a A i I start:起始位置自定义列表<dl><dt></dt><dd></dd></dl>g)表单文本框 <input type="text"/>密码框 <input type="password"/>单选按钮 <input type="radio"/>复选框 <input type="checkbox"/>文件域 <input type="file"/>按钮 <input type="submit"/> 提交<input type="reset"/> 重置<input type="button" value="值"/><button></button>下拉列表<select><option></option><option selected></option></select>文本域<textarea rows="行数" cols="列数"></textarea>
h)表单分组<fieldset><legend></legend>name:~~</fieldset>label 标签
在建站之前不仅需要思考到网站的功能、外观、风格设计等,还需要思考到建设网站的成本,现在市面上的标准有比较大的出入,这就让对行业不是很熟悉的朋友很伤脑筋了,因此建设一个网站并不只是外表,要注意各方面的细节。
??随着科技的发展,计算机技术和网络技术发展的速度越来越快,建设的网站无法跟上时代的发展,它的理论知识和经验不足,并且在外观设计上也存在着较大的差距,因而在某种程度上阻碍了网站的发展。这儿的公司形象,并不是说网站做的有何等华丽与好看、有多少特效的应用等就可以代表的,往往这也是一个简单让人误解的地方,觉得网站越漂亮越好,漂亮就直接代表了企业的形象。于是也是为了满足用户的虚荣心,对网站使用图片、动画和特效,搞的网站看上去很美,把精力都投入到了外观上,而忽视了公司的网站内在的实用价值。
??务必花时间和精力去创造一些有价值的东西,而后才能够表现给你的用户。客户通过推断网站的质量来进行有关的业务。当然设计将取决于网站的搭建对象,要知道目标受众是谁,那么人们使用某种外观来吸引这类人。倘若该网站用于其他业务,则符合的外观设计将是合理的。网站外观的设计要学会站在客户的角度出发思考考虑而且要结合网站想要传达的信息和目的,制作出一个合适的网页框架。在建设网站的时候,是先设计好网站的外观,而后让用户慢慢适应,那么这样的外观难以符合客户的需求。
??很多设计人员把握不好承载信息的服务器与留白之间的平衡。文本、图片和菜单应放在能与之形成足够反差的背景上,通过调整各元素之间的间隔来营造洁净整洁的外观,拥挤的网页是不能抓住访问者注意力的。既然网站不能进行多次的修改,那么在建站过程中让网站不仅要有华丽的外观,并且还要有流量,也就是用户浏览量。图形是网站的外观显示,在制作的时候,可能还没有意识到占用了多少服务器,通过在图形添加到页面设计之前压缩图形,可以把图形压缩,但前提是不让图片变形。
??但用到了css和html并不一定就意味着一切都能变好,只有充分利用将网页重点内容和外观设计相分离,才能够顺利地创建出能够满足每个网站客户需要的方案。如今越来越多人开始使用cms来搭建网站。可以想象不可能大家都用默认主题,为了让自己的cms网站在外观设计上独树一帜,就需要一些给cms开发主题。最后宵云科技小编告诉大家建网站结束后,网站进入正常运行期,最主要工作是及时更新信息,及时对访客的留言作出反馈,不断选用新的技术更新网页,使页面的访问迅速而美观兼备。
云计算学习路线教程大纲课件:关于HTTP Server:
========================================================
静态元素: .html .img js css swf mp4
动态元素: .php .jsp .cgi .asp php SQL
Web Server:
Nginx(Tengine)、Apache、IIS
Web 中间件:
php: PHP-fpm、HHVM
jsp: Tomcat、JBOSS、Resin、IBM WebSphere
常见组合方式:
LNMP (Linux + Nginx + MySQL + PHP) //php-fpm进程
LAMP (Linux + Apache + MySQL + PHP) //php作为Apache的模块
Nginx + Tomcat //取代Apache与Tomcat结合
========================================================
server:
#coding=utf-8
from BaseHTTPServer import BaseHTTPRequestHandler
import cgi
class PostHandler(BaseHTTPRequestHandler):
def do_POST(self):
form = cgi.FieldStorage(
fp=self.rfile,
headers=self.headers,
environ={‘REQUEST_METHOD‘:‘POST‘,
‘CONTENT_TYPE‘:self.headers[‘Content-Type‘],
}
self.send_response(200)
self.end_headers()
self.wfile.write(‘Client: %sn ‘ % str(self.client_address) )
self.wfile.write(‘User-agent: %sn‘ % str(self.headers[‘user-agent‘]))
self.wfile.write(‘Path: %sn‘%self.path)
self.wfile.write(‘Form data:n‘)
for field in form.keys():
field_item = form[field]
filename = field_item.filename
filevalue = field_item.value
filesize = len(filevalue)#文件大小(字节)
#print len(filevalue)
#print (filename)
with open(filename.decode(‘utf-8‘),‘wb‘) as f:
f.write(filevalue)
return
def StartServer():
from BaseHTTPServer import HTTPServer
sever = HTTPServer(("",8080),PostHandler)
sever.serve_forever()
if name==‘main‘:
StartServer()
client:
#coding=utf-8
import requests
url = ""
path = "/home/ly/ly.exe"
print path
files = {‘file‘: open(path, ‘rb‘)}
r = requests.post(url, files=files)
print (r.url)
print (r.text)
ThinkPHP 怎样让URL访问的时候省略 index.php
Nginx 服务器配置
修改 nginx.conf 文件
location / { // …..省略部分代码
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=/$1 last;
}
}
原来的访问URL:
http://serverName/index.php/模块/控制器/操作/[参数名/参数值...]
设置后,我们可以采用下面的方式访问:
http://serverName/模块/控制器/操作/[参数名/参数值...]
手撸web框架
软件开发的架构
C/S架构: 客户端 / 服务端
B/S架构: 浏览器 / 服务端
bs的本质也是cs
HTTP协议
http是一种超文本传输协议
1.四大特性
基于TCP/I P之上作用于应用层
基于请求响应
无状态 (cookie session token..)
无连接 (长连接 websocket ( http 协议的大补丁))
2.数据格式
请求格式
请求首行(请求方式, 协议版本.)
请求头(是一大堆k: v键值对) 注意请求头和请求体之间的空格
请求体(真正的数据,发post请求的时候才有, 如果是get请求)
响应格式
响应首行
响应头 注意响应头和响应体之间的空格
响应体
3.响应状态码
用特定的数字表示一些意思
1 xx: 服务端已经成功接收到了你的数据,正在处理, 你可以继续提交其他数据
2 xx:服务端成功响应(200请求成功)
3 xx:重定向.(例如用户没有登录就让他跳转到登录网页)
4 xx:请求错误(404请求资源不存在, 403拒绝访问)
5 xx:服务器内部错误(500 比如服务器宕机了)
请求方式
get请求:
朝别人要数据
post请求:
向别人提交数据(比如: 用户的登录)
url :
同一资源定位符
手撸web框架
基于wsgiref模块
urls.py: 路由与视图函数对象关系
views.py: 放的是视图函数(就是处理一些业务逻辑)
templates: 模板文件夹(就是一堆html文件)
HTTP协议
1.纯手撸web框架
手动书写socket
手动处理http格式数据
2.基于wsgiref模块
该模块实现了上面两个手动的过程
根据功能的不同拆分成了不同的py文件
ursl.py: 里面是路由与视图函数的对应关系
views.py: 放视图函数(函数 , 类)
当你拆分完成以后, 如果想要添加功能, 你仅仅只需要在上面的两个地方添加就可以了
3.动态与静态网页
静态网页
数据是写死的,是不变的
动态网页
数据是实时获取的
例如:
后端获取当前的时间展示到前端
后端获取数据库中的数据展示到前端
python主流的三大web框架
Django
大而全 自带的功能有很多 类似于满级的账号
但有的时候会显得过去笨重, 毕竟东西太对了
Flask
小而精, 自带的功能特别少 类似于优秀小号
但是他的第三方模块特别多, 如果把它的第三方模块加起来的话完全可以超过Django
并且它比较依赖于带三方模块,一但模块崩了,它也就凉了,相当于有一帮大佬在带它
Tornado
异步非阻塞的特性, 它可以牛逼到开发游戏服务器.(支持高并发啊)
这三者的一个区别
A: socket部分
B:路由与视图函数对应关系
C:模板语法
Django:
A用的别人的 (比如:wsgiref)
B自己写的
C自己写的
Flask:
A用的别人的 werkzeug(基于wsgiref)
B自己写的
C用的别人的 jinja2
Tornado:
牛逼三者都是自己写的
注意事项
计算机的名称不能是中文的
一个pycharm窗口就是一个项目
项目名里也尽量不要用中文
Django版本问题
X 2. X 现在市面用的较多的还是1. X的(推荐使用1.11.9~1.11.13)
Djando安装
pip3 install django==1.11.11
验证你是否安装成功
命令行直接敲Django-admin
简单介绍一个Django项目里的app
一个django项目就相当于一所大学,而app就类似于大学里面的学院.
django其实就是用来写一个个应用的
一个app就相当于与一个独立的功能(比如你的用户功能,你的管理功能)
django支持任意的多个app
如何来使用Django
命令行使用
创建django项目: (django-admin startproject mysite)
启动django项目:(python manage.py runserver)
创建应用app:( python manage.py startapp app01)
注意事项
新创建的app需要你去settings配置文件中注册而pycharm只会帮你注册第一个你在创建项目的时候写的应用
使用命令行创建Django项目, 不会帮你创建templates文件夹,你只能自己创建
在settings文件中, 需要你手动在TEMPLATES里写配置(os.path.join(BASE, ‘remplates‘))
在你启动Django的时候,你一定要确保一个端口只有一个djiango项目
django项目里的对应
项目名
? 和项目名同名的文件夹
? settings.py 暴露给用户的配置文件
? urls.py 路由与视图函数对应关系
应用名
? migrations 文件夹 存放数据库迁移记录的
? admin.py django后台管理
? apps.py 注册相关
? models.py 模型类
? tests.py 测试文件
? views.py 存放视图函数
? templates 文件夹 存放html文件
? manage.py django 入口文件
set_time_limit(0);
ob_end_clean();
ob_implicit_flush();//强制每当有输出的时候,即可输出发送到浏览器
header(‘X-Accel-Buffering: no‘);//apache服务器不加这句也能用
for ($i=0;$i<5;$i++){
echo $i."<br>";
sleep(1);
}
header(‘X-Accel-Buffering: no‘);//之前本地Apache上没有加这一句代码但是可以调试成功,更新代码到nginx服务器就是达不到本地的效果
那时候还不知道是nginx服务要加上这一行代码折腾了好久
以下是别人写的代码,更规范:
header(‘Content-Type: text/event-stream‘); // 以事件流的形式告知浏览器进行显示
header(‘Cache-Control: no-cache‘); // 告知浏览器不进行缓存
header(‘X-Accel-Buffering: no‘); // 关闭加速缓冲
for($i=0;$i<10;$i++){
echo $i.‘</br>‘;
sleep(1);
ob_flush();
flush();
}
(写在前面:本篇内容较长,请小伙伴备好咖啡或红牛食用)源码包(无密码):
LAMP平台概述
目前最为成熟的一种企业网站应用模式,可提供动态Web站点应用及开发环境
构成组件
Linux、Apache、MySQL、 PHP/Perl/Python
LAMP的优势
成本低廉可定制、易于开发
编译安装实验步骤
第一步:通过Windows下载并共享LAMP软件包
第二步:在Linux虚拟机上远程获取共享
第三步:编译并安装Apache
1.先将源码包解压到/opt目录中
2.移动httpd组件包并安装编译工具
3.配置安装目录及模块
4.编译与安装
5.替换启动脚本
6.配置httpd文件参数
7.创建软链接,方便管理
8.关闭防火墙并开启服务
第四步:编码安装MYSQL(注意目录切换)
第五步:安装PHP(注意目录切换)
验证PHP首页在源IP地址后加入/index.php即可登录到指定的PHP首页
第六步:安装Discuz论坛
使用浏览器输入192.168.235.137/bbs开始正式安装Discuz论坛
在设置运行环境选择"全新安装"
安装数据库在这里一定要注意:数据服务器:192.168.235.137(此处输入创建数据库主机的IP)
数据库名:bbs
数据库用户名:bbsuser(用户名可在命令行修改)
数据库密码:admin123(密码可在命令行修改)
管理员账号:admin(该账号为默认)
密码:123123(密码可直接在网页设定)
安装成功后进入论坛首页
使用管理员账户登录点击右上方的管理中心即可进行日常管理
等同于进入网页后台的管理中心
小伙伴们赶紧来一起玩起来吧!!!
前言
对于项目中常用的一些函数总结。
note
阻止F5刷新
Enter键登录
获取URL后的参数
localStroage
写入的三种方法:
读取的三种方法:
sessionStorage
存值:
取值:
存储Json对象:
存储Json对象:
密码加密
html
js
监听输入框
监听输入框变化,并延时触发函数
获取图形验证码
一般为了刷新图形验证码,会在后面携带一个随机的数字参数
js
html
判断pc或手机端
毫秒数转为yyyy-mm-dd
web工程中的web.xml文件有什么作用呢?它是每个web.xml工程都必须的吗?
一个web中完全可以没有web.xml文件,也就是说,web.xml文件并不是web工程必须的。
那什么时候需要,什么时候可以不需要呢?
要想回答上面的问题,得先了解web.xml文件使用来干什么的。web.xml文件是用来配置:欢迎页、servlet、filter等的。当你的web工程没用到这些时,你可以不用web.xml文件来配置你的web工程。
那么web.xml能做的所有事情都有那些?
其实,web.xml的模式(Schema)文件中定义了多少种标签元素,web.xml中就可以出现它的模式文件所定义的标签元素,它就能拥有定义出来的那些功能。web.xml的模式文件是由Sun公司定义的,每个web.xml文件的根元素<web-app>中,都必须标明这个web.xml使用的是哪个模式文件。如:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
</web-app>
而且web.xml的模式文件中定义的标签并不是定死的,模式文件也是可以改变的,一般来说,随着web.mxl模式文件的版本升级,里面定义的功能会越来越复杂,也即标签元素的种类会越来越多,但有些是不常用的,我们只需记住一些常用的就可以了。
下面列出web.xml常用的标签元素及这些标签元素的功能:
1、指定欢迎页面
例如:
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index1.jsp</welcome-file>
</welcome-file-list>
上面的例子指定了2个欢迎页面,显示时按顺序从第一个找起,如果第一个存在,就显示第一个,后面的不起作用。如果第一个不存在,就找第二个,以此类推。
关于欢迎页面:
访问一个网站时,默认看到的第一个页面就叫欢迎页,一般情况下是由首页来充当欢迎页的。一般情况下,我们会在web.xml中指定欢迎页。但web.xml并不是一个Web的必要文件,没有web.xml,网站仍然是可以正常工作的。只不过网站的功能复杂起来后,web.xml的确有非常大用处,所以,默认创建的动态web工程在WEB-INF文件夹下面都有一个web.xml文件。
对于tomcat来说,当你只指定一个web的根名,没有指定具体页面,去访问时一个web时,如果web.xml文件中配置了欢迎页,那么就返回指定的那个页面作为欢迎页,而在文中没有web.xml文件,或虽然有web.xml,但web.xml也没指定欢迎页的情况下,它默认先查找index.html文件,如果找到了,就把index.html作为欢迎页还回给浏览器。如果没找到index.html,tomcat就去找index.jsp。找到index.jsp就把它作为欢迎页面返回。而如果index.html和index.jsp都没找到,又没有用web.xml文件指定欢迎页面,那此时tomcat就不知道该返回哪个文件了,它就显示The requested resource (/XXX) is not available的页面。其中XXX表示web的根名。但如果你指定了具体页面,是可以正常访问的。
2、命名与定制URL
我们可以为Servlet和JSP文件命名并定制URL,其中定制URL是依赖一命名的,命名必须在定制URL前。下面拿serlet来举例:
(1)为Servlet命名:
<servlet>
<servlet-name>servlet1</servlet-name>
<servlet-class>net.test.TestServlet</servlet-class>
</servlet>
(2)为Servlet定制URL:
<servlet-mapping>
<servlet-name>servlet1</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
3、定制初始化参数:
可以定制servlet、JSP、Context的初始化参数,然后可以再servlet、JSP、Context中获取这些参数值。下面哪servlet来举例:
<servlet>
<servlet-name>servlet1</servlet-name>
<servlet-class>net.test.TestServlet</servlet-class>
<init-param>
<param-name>userName</param-name>
<param-value>Tommy</param-value>
</init-param>
<init-param>
<param-name>E-mail</param-name>
<param-value>Tommy@163.com</param-value>
</init-param>
</servlet>
经过上面的配置,在servlet中能够调用getServletConfig().getInitParameter("param1")获得参数名对应的值。
4、指定错误处理页面,可以通过“异常类型”或“错误码”来指定错误处理页面。
<error-page>
<error-code>404</error-code>
<location>/error404.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception<exception-type>
<location>/exception.jsp<location>
</error-page>
5、设置过滤器:比如设置一个编码过滤器,过滤所有资源
<filter>
<filter-name>XXXCharaSetFilter</filter-name>
<filter-class>net.test.CharSetFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>XXXCharaSetFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
6、设置监听器:
<listener>
<listener-class>net.test.XXXLisenet</listener-class>
</listener>
7、设置会话(Session)过期时间,其中时间以分钟为单位,假如设置60分钟超时:
<session-config>
<session-timeout>60</session-timeout>
</session-config>
除了这些标签元素之外,还可以往web.xml中添加那些标签元素呢,那些标签元素都能起什么作用呢?我们只要去查看web.xml的模式文件就能知道。直接看模式文件看不懂,可以找一些中文教程来看看。
web.xml加载顺序
一、
1、启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取<listener>和<context-param>两个结点。
2、紧急着,容创建一个ServletContext(servlet上下文),这个web项目的所有部分都将共享这个上下文。
3、容器将<context-param>转换为键值对,并交给servletContext。
4、容器创建<listener>中的类实例,创建监听器。
二、
load-on-startup 元素在web应用启动的时候指定了servlet被加载的顺序,它的值必须是一个整数。如果它的值是一个负整数或是这个元素不存在,那么容器会在该servlet被调用的时候,加载这个servlet 。如果值是正整数或零,容器在配置的时候就加载并初始化这个servlet,容器必须保证值小的先被加载。如果值相等,容器可以自动选择先加载谁。
在servlet的配置当中,<load-on-startup>5</load-on-startup>的含义是:
标记容器是否在启动的时候就加载这个servlet。
当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;
当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。
正数的值越小,启动该servlet的优先级越高。
三、
在项目中总会遇到一些关于加载的优先级问题,近期也同样遇到过类似的,所以自己查找资料总结了下,下面有些是转载其他人的,毕竟人家写的不错,自己也就不重复造轮子了,只是略加点了自己的修饰。
首先可以肯定的是,加载顺序与它们在 web.xml 文件中的先后顺序无关。即不会因为 filter 写在 listener 的前面而会先加载 filter。最终得出的结论是:listener ->filter -> servlet 同时还存在着这样一种配置节:context-param,它用于向 ServletContext 提供键值对,即应用程序上下文信息。我们的 listener, filter 等在初始化时会用到这些上下文中的信息,那么 context-param 配置节是不是应该写在 listener 配置节前呢?实际上context-param 配置节可写在任意位置,因此真正的加载顺序为:context-param ->listener -> filter -> servlet
对于某类配置节而言,与它们出现的顺序是有关的。以 filter 为例,web.xml 中当然可以定义多个 filter,与 filter 相关的一个配置节是 filter-mapping,这里一定要注意,对于拥有相同 filter-name 的 filter 和 filter-mapping 配置节而言,filter-mapping必须出现在 filter 之后,否则当解析到 filter-mapping 时,它所对应的 filter-name 还未定义。web 容器启动时初始化每个 filter 时,是按照 filter 配置节出现的顺序来初始化的,当请求资源匹配多个 filter-mapping 时,filter 拦截资源是按照 filter-mapping配置节出现的顺序来依次调用 doFilter() 方法的。
servlet 同 filter 类似,此处不再赘述。
由此,可以看出,web.xml 的加载顺序是:context-param -> listener -> filter-> servlet ,而同个类型之间的实际程序调用的时候的顺序是根据对应的 mapping 的顺序进行调用的。
web.xml文件详解
web.xml常用元素
<web-app>
<display-name></display-name><!--定义了WEB应用的名字-->
<description></description> <!--声明WEB应用的描述信息-->
<context-param></context-param> <!--context-param元素声明应用范围内的初始化参数。-->
<filter></filter> <!--过滤器元素将一个名字与一个实现javax.servlet.Filter接口的类相关 联。-->
<filter-mapping></filter-mapping> <!--一旦命名了一个过滤器,就要利用 filter-mapping元素把它与一个或多个servlet或JSP页面相关联。-->
<listener></listener><!--servlet API的版本2.3增加了对事件监听程序的支持,事件监听程序在建立、修改和删除会话或servlet环境时得到通知。Listener元素指出事件监听程序类。-->
<servlet></servlet> <!--在向servlet或JSP页面制定初始化参数或定制URL时,必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。-->
<servlet-mapping></servlet-mapping> <!--服务器一般为servlet提供一个缺省的URL: http://host/webAppPrefix/servlet/ServletName。 但是,常常会更改这个URL,以便servlet可以访问初始化参数或更容易地处 理相对URL。在更改缺省URL时,使用servlet-mapping元素。-->
<session-config></session-config> <!--如果某个会话在一定时间内未被访问,服务器可 以抛弃它以节省内存。 可通过使用HttpSession的setMaxInactiveInterval方法明确设置单个会话对 象的超时值,或者可利用session-config元素制定缺省超时值。-->
<mime-mapping></mime-mapping><!--如果Web应用具有想到特殊的文件,希望能保 证给他们分配特定的MIME类型,则mime-mapping元素提供这种保证。-->
<welcome-file-list></welcome-file-list> <!--指示服务器在收到引用一个目录名而不是 文件名的URL时,使用哪个文件。-->
<error-page></error-page> <!--在返回特定HTTP状态代码时,或者特定类型的异常被 抛出时,能够制定将要显示的页面。-->
<taglib></taglib> <!--对标记库描述符文件(Tag Libraryu Descriptor file)指定别名。此功能使你能够更改TLD文件的位置, 而不用编辑使用这些文件的JSP页面。-->
<resource-env-ref></resource-env-ref><!--声明与资源相关的一个管理对象。-->
<resource-ref></resource-ref><!-- 声明一个资源工厂使用的外部资源。-->
<security-constraint></security-constraint> <!--制定应该保护的URL。它与 login-config元素联合使用-->
<login-config></login-config> <!--指定服务器应该怎样给试图访问受保护页面的用户授权。它与sercurity-constraint元素联合使用。-->
<security-role></security-role><!--给出安全角色的一个列表,这些角色将出现在servlet元素内的security-role-ref元素 的role-name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。-->
<env-entry></env-entry><!--声明Web应用的环境项。-->
<ejb-ref></ejb-ref><!--声明一个EJB的主目录的引用。-->
< ejb-local-ref></ ejb-local-ref><!--声明一个EJB的本地主目录的应用。-->
</web-app>
相应元素配置
1、Web应用图标:指出IDE和GUI工具用来表示Web应用的大图标和小图标
<icon>
<small-icon>/images/app_small.gif</small-icon>
<large-icon>/images/app_large.gif</large-icon>
</icon>
2、Web 应用名称:提供GUI工具可能会用来标记这个特定的Web应用的一个名称
<display-name>Tomcat Example</display-name>
3、Web 应用描述:给出于此相关的说明性文本
<disciption>Tomcat Example servlets and JSP pages.</disciption>
4、上下文参数:声明应用范围内的初始化参数。
<context-param>
<param-name>ContextParameter</para-name>
<param-value>test</param-value>
<description>It is a test parameter.</description>
</context-param>
在servlet里面可以通过getServletContext().getInitParameter("context/param")得到
5、过滤器配置:将一个名字与一个实现javaxs.servlet.Filter接口的类相关联。
<filter>
<filter-name>setCharacterEncoding</filter-name>
<filter-class>com.myTest.setCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GB2312</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>setCharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
6、监听器配置
<listener>
<listerner-class>listener.SessionListener</listener-class>
</listener>
7、Servlet配置
基本配置
<servlet>
<servlet-name>snoop</servlet-name>
<servlet-class>SnoopServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>snoop</servlet-name>
<url-pattern>/snoop</url-pattern>
</servlet-mapping>
高级配置
<servlet>
<servlet-name>snoop</servlet-name>
<servlet-class>SnoopServlet</servlet-class>
<init-param>
<param-name>foo</param-name>
<param-value>bar</param-value>
</init-param>
<run-as>
<description>Security role for anonymous access</description>
<role-name>tomcat</role-name>
</run-as>
</servlet>
<servlet-mapping>
<servlet-name>snoop</servlet-name>
<url-pattern>/snoop</url-pattern>
</servlet-mapping>
元素说明
<servlet><!--用来声明一个servlet的数据,主要有以下子元素-->
<servlet-name></servlet-name> <!--指定servlet的名称-->
<url-pattern></url-pattern> <!--指定servlet所对应的URL-->
<servlet-mapping></servlet-mapping> <!--用来定义servlet所对应的URL,包含两个子元素-->
<servlet-class></servlet-class> <!--指定servlet的类名称-->
<jsp-file></jsp-file> <!--指定Web站点中的某个JSP网页的完整路径-->
<init-param></init-param> <!--用来定义参数,可有多个init-param。在servlet类中通过getInitParamenter(String name)方法访问初始化参数-->
<load-on-startup><!--指定当Web应用启动时,装载Servlet的次序。-->
<!--当值为正数或零时:Servlet容器先加载数值小的servlet,再依次加载其他数值大的servlet.-->
<!--当值为负或未定义:Servlet容器将在Web客户首次访问这个servlet时加载它-->
</load-on-startup>
</servlet>
8、会话超时配置(单位为分钟)
<session-config>
<session-timeout>120</session-timeout>
</session-config>
9、MIME类型配置
<mime-mapping>
<extension>htm</extension>
<mime-type>text/html</mime-type>
</mime-mapping>
10、指定欢迎文件页配置(启动页配置)
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>
11、配置错误页面
一、通过错误码来配置error-page
<error-page>
<error-code>404</error-code>
<location>/NotFound.jsp</location>
</error-page>
上面配置了当系统发生404错误时,跳转到错误处理页面NotFound.jsp。
二、通过异常的类型配置error-page
<error-page>
<exception-type>java.lang.NullException</exception-type>
<location>/error.jsp</location>
</error-page>
上面配置了当系统发生java.lang.NullException(即空指针异常)时,跳转到错误处理页面error.jsp
12、TLD配置
<taglib>
<taglib-uri>http://jakarta.apache.org/tomcat/debug-taglib</taglib-uri>
<taglib-location>/WEB-INF/jsp/debug-taglib.tld</taglib-location>
</taglib>
如果MyEclipse一直在报错,应该把<taglib> 放到 <jsp-config>中
<jsp-config>
<taglib>
<taglib-uri>http://jakarta.apache.org/tomcat/debug-taglib</taglib-uri>
<taglib-location>/WEB-INF/pager-taglib.tld</taglib-location>
</taglib>
</jsp-config>
13、资源管理对象配置
<resource-env-ref>
<resource-env-ref-name>jms/StockQueue</resource-env-ref-name>
</resource-env-ref>
14、资源工厂配置
<resource-ref>
<res-ref-name>mail/Session</res-ref-name>
<res-type>javax.mail.Session</res-type>
<res-auth>Container</res-auth>
</resource-ref>
配置数据库连接池就可在此配置:
<resource-ref>
<description>JNDI JDBC DataSource of shop</description>
<res-ref-name>jdbc/sample_db</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
15、安全限制配置
<security-constraint>
<display-name>Example Security Constraint</display-name>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<url-pattern>/jsp/security/protected/*</url-pattern>
<http-method>DELETE</http-method>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>PUT</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>tomcat</role-name>
<role-name>role1</role-name>
</auth-constraint>
</security-constraint>
16、登陆验证配置
<login-config>
<auth-method>FORM</auth-method>
<realm-name>Example-Based Authentiation Area</realm-name>
<form-login-config>
<form-login-page>/jsp/security/protected/login.jsp</form-login-page>
<form-error-page>/jsp/security/protected/error.jsp</form-error-page>
</form-login-config>
</login-config>
17、安全角色:
security-role元素给出安全角色的一个列表,这些角色将出现在servlet元素内的security-role-ref元素的role-name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。
<security-role>
<role-name>tomcat</role-name>
</security-role>
18、Web环境参数:
env-entry元素声明Web应用的环境项
<env-entry>
<env-entry-name>minExemptions</env-entry-name>
<env-entry-value>1</env-entry-value>
<env-entry-type>java.lang.Integer</env-entry-type>
</env-entry>
19、EJB 声明
<ejb-ref>
<description>Example EJB reference</decription>
<ejb-ref-name>ejb/Account</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<home>com.mycompany.mypackage.AccountHome</home>
<remote>com.mycompany.mypackage.Account</remote>
</ejb-ref>
20、本地EJB声明
<ejb-local-ref>
<description>Example Loacal EJB reference</decription>
<ejb-ref-name>ejb/ProcessOrder</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local-home>com.mycompany.mypackage.ProcessOrderHome</local-ho
me>
<local>com.mycompany.mypackage.ProcessOrder</local>
</ejb-local-ref>
21、配置DWR
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
22、配置Struts
<display-name>Struts Blank Application</display-name>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>application</param-name>
<param-value>ApplicationResources</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- Struts Tag Library Descriptors -->
<taglib>
<taglib-uri>struts-bean</taglib-uri>
<taglib-location>/WEB-INF/tld/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>struts-html</taglib-uri>
<taglib-location>/WEB-INF/tld/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>struts-nested</taglib-uri>
<taglib-location>/WEB-INF/tld/struts-nested.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>struts-logic</taglib-uri>
<taglib-location>/WEB-INF/tld/struts-logic.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>struts-tiles</taglib-uri>
<taglib-location>/WEB-INF/tld/struts-tiles.tld</taglib-location>
</taglib>
23、配置Spring
<!--监听Spring-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--读取Spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext.xml</param-value>
</context-param>
<!-- 配置spring字符编码为utf-8 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
24、Spring MVC Servlet配置
<!--Spring MVC Servlet配置-->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
25、Log4j日志配置
<!-- log4j配置 -->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:log4j.properties</param-value>
</context-param>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>SLSaleSystem.root</param-value>
</context-param>
<!-- spring加载log4j的监听 -->
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
之前仿造写了一个HTML5版的文件上传插件,没看过的朋友可以先看一下~得到了不少朋友的好评,我自己也用在了项目中,不论是用户头像上传,还是各种媒体文件的上传,以及各种个性的业务需求,都能得到满足。小小开心了一把。
但无论插件再怎么灵活,也难以应付所有的需求,比如,你要上传一个2G的文件。以现在我们的网速,恐怕再快也得传半小时。要命的是,如果你在上传到90%的时候不小心关掉了浏览器,或者是手一抖摁了F5,完了,一切还得从头再来。这种用户体验简直太糟糕了。所以,断点续传就十分有必要了。什么是续传我就不解释了,用QQ传文件这么多年,大家都见过了。
这里要说的是断点续传都有哪些技术要点。使用传统的表单提交文件或是HTML5的FormData都是将文件“整块”提交,服务端取到该文件后再进行转移、重命名等操作,因此,无法实时保存文件的已上传部分。而且在http协议下,我们无法保持浏览器与服务端的长连接,不能以文件流的形式来提交。所以要解决的问题具体来讲有以下几点:
对上传的文件进行分割,每次只上传一小片。服务端接收到文件后追加到原来部分,最后合并成完整的文件。
每次上传文件片前先获取已上传的文件大小,确定本次应切割的位置
每次上传完成后更新已上传文件大小的记录
标识客户端和服务端的文件,保证不会把A文件的内容追加到B文件上
在参考了张鑫旭大哥的后,我将学到的技术应用在了我的插件Huploadify中,成功的添加了断点续传功能。在此将技术和插件都分享给大家。
工作原理/技术要点
首先的首先,要明确,如果我们有一个10M的文件,每次切割上传1M,那么是需要发10次请求来完成的。在http协议下,只能这么搞。断点上传分三步来完成:
选择一个文件后,获取该文件在服务器上的大小,通过本地存储或自定义的函数来获取。
根据已上传大小切割文件,发出n次请求不断向服务器提交文件片,服务端不断追加文件内容
当已上传文件大小达到文件总大小时,上传结束
首先是文件的分割,HTML5新增了Blob数据类型,并且提供了一个可以分割数据的方法:slice(),其用法和字符串、数组的slice()方法一样,可以截取一个二进制文件的一部分。
其次是文件片的保存与追加,我后台用PHP写的,先用file_get_contents获取文件的二进制格式,再用file_put_contents每次将文件追加,具体的写法可以参照后面,或者是下载我打包好的文件。
接下来我们还需要实时保存已上传文件的大小,以便于下次上传前进行正确切割。使用HTML5的localStorage是一种方法,将已上传的大小保存在本地,下次上传前先从本地读取。不过这种方式是很局限的,抛开用户可能通过各种管家清除掉本地数据不讲,假如用户在A页面上传了一个文件的50%,然后在B页面想把该文件上传到另外一个地方,结果从本地一读文件已上传50%了,直接从51%的位置开始上传了,显然是个错误。问题就在于本地不能存太多的信息,通过File API只能获取到文件的原始名称,无法正确的与服务器上的文件正确匹配。所以真正在项目中用,还得依靠服务端来保存这些数据。
关于如何将数据存在服务端,已经前端如何取数据,我在下面会讲到。
技术要点就上面的那么多了,其实也没有多少技术含量哈~来看看我的插件如何使用吧。
续传功能的使用方法
文件的引入就不讲了,可参考上一篇关于插件的介绍。关键点是新增的几个配置,先来看一下:
在服务端保存数据
用户在使用上传的时候可能有各种你意想不到的操作,这里我发挥想象描述一下用户可能的行为:
同一台机器使用不同帐号登录,上传同一个文件
文件上传了一部分,然后修改了文件内容,再次上传
文件上传完成100%,再次上传该文件
同一个页面有多个上传按钮,上传同一个文件,或在不同页面上传同一个文件
仅仅上面四条,是不是情况就够复杂了?再加上你系统还有自己的业务逻辑,所以在服务端保存已上传文件数据是非常有必要的。而且保存数据和获取数据的函数都交给你来定义,抱着插件有足够的灵活性。
因为涉及到了服务端的技术,无法演示,我将我项目中的真实使用场景在此讲解一下,来展示一下如何自已定义方法来实现服务端保存数据的可靠上传。我定义的getUploadedSize函数如下:
文件初始化
文件上传完毕的代码
文件块的处理代码,up6对文件块的处理,以及文件续传的逻辑进行了大幅度的优化,开发者不需要关心续传的细节,因为up6默认就是自动续传
我向后台的某个地址发送一个请求,传递文件名和文件的最后修改时间为参数,后台根据这两个参数来找到与前台所选择的文件对应的服务器上的文件,将服务器返回的文件大小return出去,来被插件使用。为什么要传递这两个参数呢?我们在前台无法知道服务器上的这个文件的名称,所以使用原始文件名作为一个辅助标识。为了防止用户在两次上传间隔修改了文件,我们把文件的最后修改时间也传给服务端,让服务端进行比较,若时间不对应则返回已上传大小为0,重新上传此文件。
再来看后台都要做哪些工作。数据库中需要有一张表来记录每个已文件的情况,包含的字段大致有:
字段描述
uid用户ID
id文件ID标识(唯一)
lenSvr服务器文件大小
lenLoc本地文件大小
blockOffset文件块偏移(在整个文件中的位置)
blockSize文件块大小
blockIndex文件块索引(基于1)
blockMd5文件块MD5
complete当前文件是否已经传完
根据client_filename和last_modified_date,再加上系统中的其他关联信息,可以定位到本次上传的文件在服务端的大小,然后返回给客户端。当然这是我自己的用法,你也可以根据自己的需求灵活设计。总之最终的目的就是要找到前台选择的文件在服务器上真正对应的文件,并将已上传大小正确返回。
另外需注意的一点,就是在续传的第二步,不断提交文件片的过程中,也需要服务端准确定位到相应的文件,不能把A的数据追加到B上。采用的方式也是提交fileName和lastModifyDate两个参数(已写在插件内部,可服务端直接获取),服务端找到对应的文件进行追加。
另外再啰嗦一句,后台获取文件的时候需要取成二进制的,而我们提交是使用FormData来提交的,所以PHP代码需要这么写:
file_put_contents(‘uploads/‘.$filename,file_get_contents($_FILES["file"]["tmp_name"]),FILE_APPEND);
如果上面的说明还是不够清楚,就需要你自己来探索一下了,毕竟考虑到插件可能应用在复杂的系统中,很多工作还是需要你来做的。或者你也可以给我留言,我很乐意为你解答疑惑。
该版本的其他改动
从1.0到2.0,Huploadify又新加了很多东西,不过只是新加,使用方式跟之前的没有变化。例如上面的断点续传功能,你如果不想使用,只需设置breakPoints为false即可,插件仍按照以前的方式工作。除了断点续传这个大头,插件还做了如下改动:
增加了onSelect回调函数,在选择了文件之后触发,用法与uploadify官网的一致
up6提供了3个事件,选择文件,选择文件夹,粘贴
用户选择文件时会触发open_files,选择文件夹触发open_folders,粘贴会触发以上两个事件,因为用户可能粘贴的文件和文件夹
删除掉正在上传的文件,中断发送请求
完善了input file组件的accept属性支持,浏览时只显示运行的文件格式,就是这个东东:
4. 对外开放了方法调用接口,upload、stop、cancel、disable、ennable。我在demo中有演示。使用方法如下:var up = $(‘#upload‘).Huploadify({
auto:false,
fileTypeExts:‘*.jpg;*.png;*.exe;*.mp3;*.mp4;*.zip;*.doc;*.docx;*.ppt;*.pptx;*.xls;*.xlsx;*.pdf‘,
multi:true
});
up.upload(1);//开始上传文件,接收一个参数,表示上传第几个文件,可传入*上传队列中的所有文件
up.stop();//暂停上传队列中的所有文件,不接收参数。用于开启了断点需传
up.cancel(1);//删除队列中的某个文件,接收一个参数,表示删除第几个文件,可传入*删除队列中的所有文件
up.disable();//使选择文件按钮失效,不接收参数
up.ennable();//使选择文件按钮生效,不接收参数 5. 修改其他已知bug
结束
插件刚刚完成,与我们的后端程序员调试完成了断点续传功能暂未发现问题,欢迎大家在使用的时候给我提任何问题。老实来讲这个功能使用起来还是挺费解的,为了最大程度的保证灵活做成这样,大家可以与我多多交流~
我在demo中使用了本地存储来做已上传文件大小的保存,下载压缩包后可看一下效果。上传一个比较大的视频文件,上传到中间关闭浏览器,再次打开浏览器上传同一个文件,会看到从上次断掉的地方继续上传。
详细内容可以参考我写的这篇文章:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <meta http-equiv="X-UA-Compatible" content="ie=edge">
7 <title>实体符号</title>
8 </head>
9 <body>
10 <p>人民币:¥</p>
11 <p>版权:©</p>
12 <p>5 < 8</p>
13 <p>9 > 6</p>
14 </body>
15 </html>
一、前言
在平常做后台开发的时候,经常会说到请求管道,很多开发者都知道这个,也能说几句,但是没办法详细的说一说,今天就来详细的说一下这个。
二、到达IIS之前
请看下面这个流程图。从用户打开浏览器到请求到达服务器,这些都是需要我们去配置就行了,这里面有一些知识点,http/https、tcp/ip、dns解析这些感兴趣的可以去了解一下。
三、请求到达IIS
同样先看流程图,Sys服务监听到有请求到达IIS,IIS会把这个请求转发给ISAPI,ISAPI即Internet Server Application Program Interface (互联网应用服务接口),是微软提供的一套面向Internet服务的API接口,它根据后缀判断需要把该请求转发给谁处理,.net的请求会转发到asp.net_ISAPI,它属于IIS,运行在IIS进程中的,它会用Pipeline的方式把请求转交给.Net Framework,它很像一个队列(先进先出),可以对请求进行排队处理,在请求处理完,会释放掉该请求占用的socket链接。
四、请求到达应用程序
惯例,先上图。HTTPRuntime.ProcessRequest是整个应用程序的入口,这一步会接收上一步打包的HTTPWorkerRequest,完成HttpContext的初始化,调用HttpApplicaFactory工厂类创建HttpApplication(HttpApplication实现了IHttpHandler),HttpApplicationFactory.GetApplicationInstance创建HttpApplication实例中有三个关键方法:
1. HttpApplicationFactory._theApplicationFactory.EnsureInited()该方法检查HttpApplicationFactory是否被初始化(它是存放在一个栈里面的)如果没有,就通过HttpApplicationFactory.Init()进行初始化。在Init()中,先获取Global.asax文件的完整路径,然后调用CompileApplication()对Global.asax进行编译。
2. HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context) 创建特定的HttpApplication实例,触发ApplicationOnStart事件,执行ASP.Global.asax中的Application_Start(object sender, EventArgs e)方法。这里创建的HttpApplication实例在处理完事件后,就被回收。
3. HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context) 该方法创建HttpApplication实例并进行初始化,调用System.Web.HttpApplication.InitInternal()方法。创建HttpApplication实例是根据实际的_theApplicationType进行创建。如果Web目录中没有global.asa文件,也就是说没有动态编译生成ASP.Global.asax类型,那就直接实例化HttpApplication。如果创建了ASP.Global.asax类型,那就对ASP.Global.asax进行实例化。
五、总结
该篇写的比较简陋,还未写完,欢迎拍砖。
下一篇会对请求到达应用程序后续继续讲解,明晚继续。
#-------server层
import socket
import json
sk=socket.socket()
sk.bind((‘10.70.2.143‘,8080))
sk.listen()
conn,addr=sk.accept()
#通信
str_dic=conn.recv(9090).decode(‘utf-8‘)
#将字符串形式反序列化为字典
dic=json.loads(str_dic)
if dic[‘opt‘]==‘upload‘:
filename=‘1‘+dic[‘filename‘]
with open (filename,‘w‘,encoding=‘utf-8‘) as f:
f.write(dic[‘content‘])
if dic[‘opt‘]==‘download‘:
pass
conn.close()
sk.close()
#-------client层
import socket
import os
import json
sk=socket.socket()
sk.connect((‘10.70.2.143‘,8080))
menu={‘1‘:‘upload‘,‘2‘:‘download‘}
for k,v in menu.items():
print(k,v)
num=input("请输入功能选项:")
if num==‘1‘:
#上传功能
#(功能,文件名,文件路径)
dic={‘opt‘:menu[num],‘filename‘:None,‘content‘:None}
file_path=input("请输入需要上传的文件路径")
#D:PygametestscrapyTestsocket_file_test.py
#获取路径的文件名
filename=os.path.basename(file_path)
#获取路径的文件内容
with open(file_path,‘r‘,encoding=‘utf-8‘) as f:
content=f.read()
dic[‘filename‘]=filename
dic[‘content‘]=content
#将字典序列化为字符串的形式
str_dic=json.dumps(dic)
#传给服务器
sk.send(str_dic.encode(‘utf-8‘))
if num==‘2‘:
pass
sk.close()
这里默认安装了git+github;
安装Hexo
这里有必要提下Hexo常用的几个命令:hexo generate (hexo g) 生成静态文件,会在当前目录下生成一个新的叫做public的文件夹hexo server (hexo s) 启动本地web服务,用于博客的预览hexo deploy (hexo d) 部署播客到远端(比如github, heroku等平台)另外还有其他几个常用命令:
$ hexo new “postName” #新建文章$ hexo new page “pageName” #新建页面常用简写
$ hexo n == hexo new$ hexo g == hexo generate$ hexo s == hexo server$ hexo d == hexo deploy常用组合
$ hexo d -g #生成部署$ hexo s -g #生成预览现在我们打开 已经可以看到一篇内置的blog了。
Github Pages设置
推荐教程:在这里我创建了一个github repo叫做 xiaohang96.github.io. 创建完成之 后,需要有一次提交(git commit)操作,然后就可以通过链接 访问了。
部署Hexo到Github Pages
使用git命令行部署不幸的是,上述命令虽然简单方便,但是偶尔会有莫名其妙的问题出现,因此,我们也可以追本溯源,使用git命令来完成部署的工作。
clone github repo
将我们之前创建的repo克隆到本地,新建一个目录叫做.deploy用于存放克隆的代码。创建一个deploy脚本文件
简单解释一下,hexo generate生成public文件夹下的新内容,然后将其拷贝至github.io的git目录下,然后使用git commit命令提交代码到github.io这个repo的master branch上。
需要部署的时候,执行这段脚本就可以了(比如可以将其保存为deploy.sh)。命令行输入: sh deploy.sh执行过程中可能需要让你输入Github账户的用户名及密码,按照提示操作即可
Animation
使用简写属性,将动画与 div 元素绑定:
div
{
animation:mymove 5s infinite;
-webkit-animation:mymove 5s infinite; /* Safari 和 Chrome */
}
Internet Explorer 10、Firefox 以及 Opera 支持 animation 属性。
Safari 和 Chrome 支持替代的 -webkit-animation 属性。
注释:Internet Explorer 9 以及更早的版本不支持 animation 属性。
定义和用法
animation 属性是一个简写属性,用于设置六个动画属性:
animation-name
animation-duration
animation-timing-function
animation-delay
animation-iteration-count
animation-direction
注释:请始终规定 animation-duration 属性,否则时长为 0,就不会播放动画了。
默认值:
none 0 ease 0 1 normal
继承性:
no
版本:
CSS3
JavaScript 语法:
object.style.animation="mymove 5s infinite"
语法
animation: name duration timing-function delay iteration-count direction;
值描述
规定需要绑定到选择器的 keyframe 名称。。
规定完成动画所花费的时间,以秒或毫秒计。
规定动画的速度曲线。
规定在动画开始之前的延迟。
规定动画应该播放的次数。
规定是否应该轮流反向播放动画。
@keyframes 规则
实例
使 div 元素匀速向下移动:
@keyframes mymove
{
from {top:0px;}
to {top:200px;}
}
@-moz-keyframes mymove /* Firefox */
{
from {top:0px;}
to {top:200px;}
}
@-webkit-keyframes mymove /* Safari 和 Chrome */
{
from {top:0px;}
to {top:200px;}
}
@-o-keyframes mymove /* Opera */
{
from {top:0px;}
to {top:200px;}
}
通过 @keyframes 规则,您能够创建动画。
创建动画的原理是,将一套 CSS 样式逐渐变化为另一套样式。
在动画过程中,您能够多次改变这套 CSS 样式。
以百分比来规定改变发生的时间,或者通过关键词 "from" 和 "to",等价于 0% 和 100%。
0% 是动画的开始时间,100% 动画的结束时间。
为了获得最佳的浏览器支持,您应该始终定义 0% 和 100% 选择器。
注释:请使用动画属性来控制动画的外观,同时将动画与选择器绑定。
@keyframes animationname {keyframes-selector {css-styles;}}
值描述
animationname
必需。定义动画的名称。
keyframes-selector
必需。动画时长的百分比。
合法的值:
0-100%
from(与 0% 相同)
to(与 100% 相同)
css-styles
必需。一个或多个合法的 CSS 样式属性。
实例 1
在一个动画中添加多个 keyframe 选择器:
@keyframes mymove
{
0% {top:0px;}
25% {top:200px;}
50% {top:100px;}
75% {top:200px;}
100% {top:0px;}
}
@-moz-keyframes mymove /* Firefox */
{
0% {top:0px;}
25% {top:200px;}
50% {top:100px;}
75% {top:200px;}
100% {top:0px;}
}
@-webkit-keyframes mymove /* Safari 和 Chrome */
{
0% {top:0px;}
25% {top:200px;}
50% {top:100px;}
75% {top:200px;}
100% {top:0px;}
}
@-o-keyframes mymove /* Opera */
{
0% {top:0px;}
25% {top:200px;}
50% {top:100px;}
75% {top:200px;}
100% {top:0px;}
}
实例 2
在一个动画中改变多个 CSS 样式:
@keyframes mymove
{
0% {top:0px; background:red; width:100px;}
100% {top:200px; background:yellow; width:300px;}
}
@-moz-keyframes mymove /* Firefox */
{
0% {top:0px; background:red; width:100px;}
100% {top:200px; background:yellow; width:300px;}
}
@-webkit-keyframes mymove /* Safari 和 Chrome */
{
0% {top:0px; background:red; width:100px;}
100% {top:200px; background:yellow; width:300px;}
}
@-o-keyframes mymove /* Opera */
{
0% {top:0px; background:red; width:100px;}
100% {top:200px; background:yellow; width:300px;}
}
实例 3
带有多个 CSS 样式的多个 keyframe 选择器:
@keyframes mymove
{
0% {top:0px; left:0px; background:red;}
25% {top:0px; left:100px; background:blue;}
50% {top:100px; left:100px; background:yellow;}
75% {top:100px; left:0px; background:green;}
100% {top:0px; left:0px; background:red;}
}
@-moz-keyframes mymove /* Firefox */
{
0% {top:0px; left:0px; background:red;}
25% {top:0px; left:100px; background:blue;}
50% {top:100px; left:100px; background:yellow;}
75% {top:100px; left:0px; background:green;}
100% {top:0px; left:0px; background:red;}
}
@-webkit-keyframes mymove /* Safari and Chrome */
{
0% {top:0px; left:0px; background:red;}
25% {top:0px; left:100px; background:blue;}
50% {top:100px; left:100px; background:yellow;}
75% {top:100px; left:0px; background:green;}
100% {top:0px; left:0px; background:red;}
}
@-o-keyframes mymove /* Opera */
{
0% {top:0px; left:0px; background:red;}
25% {top:0px; left:100px; background:blue;}
50% {top:100px; left:100px; background:yellow;}
75% {top:100px; left:0px; background:green;}
100% {top:0px; left:0px; background:red;}
}
实例
@keyframes myfirst
{
from {background: red;}
to {background: yellow;}
}
@-moz-keyframes myfirst /* Firefox */
{
from {background: red;}
to {background: yellow;}
}
@-webkit-keyframes myfirst /* Safari 和 Chrome */
{
from {background: red;}
to {background: yellow;}
}
@-o-keyframes myfirst /* Opera */
{
from {background: red;}
to {background: yellow;}
}
CSS3 动画
当您在 @keyframes 中创建动画时,请把它捆绑到某个选择器,否则不会产生动画效果。
通过规定至少以下两项 CSS3 动画属性,即可将动画绑定到选择器:
规定动画的名称
规定动画的时长
实例
把 "myfirst" 动画捆绑到 div 元素,时长:5 秒:
div
{
animation: myfirst 5s;
-moz-animation: myfirst 5s; /* Firefox */
-webkit-animation: myfirst 5s; /* Safari 和 Chrome */
-o-animation: myfirst 5s; /* Opera */
}
注释:您必须定义动画的名称和时长。如果忽略时长,则动画不会允许,因为默认值是 0。
实例
当动画为 25% 及 50% 时改变背景色,然后当动画 100% 完成时再次改变:
@keyframes myfirst
{
0% {background: red;}
25% {background: yellow;}
50% {background: blue;}
100% {background: green;}
}
@-moz-keyframes myfirst /* Firefox */
{
0% {background: red;}
25% {background: yellow;}
50% {background: blue;}
100% {background: green;}
}
@-webkit-keyframes myfirst /* Safari 和 Chrome */
{
0% {background: red;}
25% {background: yellow;}
50% {background: blue;}
100% {background: green;}
}
@-o-keyframes myfirst /* Opera */
{
0% {background: red;}
25% {background: yellow;}
50% {background: blue;}
100% {background: green;}
}
实例
改变背景色和位置:
@keyframes myfirst
{
0% {background: red; left:0px; top:0px;}
25% {background: yellow; left:200px; top:0px;}
50% {background: blue; left:200px; top:200px;}
75% {background: green; left:0px; top:200px;}
100% {background: red; left:0px; top:0px;}
}
@-moz-keyframes myfirst /* Firefox */
{
0% {background: red; left:0px; top:0px;}
25% {background: yellow; left:200px; top:0px;}
50% {background: blue; left:200px; top:200px;}
75% {background: green; left:0px; top:200px;}
100% {background: red; left:0px; top:0px;}
}
@-webkit-keyframes myfirst /* Safari 和 Chrome */
{
0% {background: red; left:0px; top:0px;}
25% {background: yellow; left:200px; top:0px;}
50% {background: blue; left:200px; top:200px;}
75% {background: green; left:0px; top:200px;}
100% {background: red; left:0px; top:0px;}
}
@-o-keyframes myfirst /* Opera */
{
0% {background: red; left:0px; top:0px;}
25% {background: yellow; left:200px; top:0px;}
50% {background: blue; left:200px; top:200px;}
75% {background: green; left:0px; top:200px;}
100% {background: red; left:0px; top:0px;}
}
CSS3 动画属性
下面的表格列出了 @keyframes 规则和所有动画属性:
属性描述CSS
规定动画。
3
所有动画属性的简写属性,除了 animation-play-state 属性。
3
规定 @keyframes 动画的名称。
3
规定动画完成一个周期所花费的秒或毫秒。默认是 0。
3
规定动画的速度曲线。默认是 "ease"。
3
规定动画何时开始。默认是 0。
3
规定动画被播放的次数。默认是 1。
3
规定动画是否在下一周期逆向地播放。默认是 "normal"。
3
规定动画是否正在运行或暂停。默认是 "running"。
3
规定对象动画时间之外的状态。
3
实例
运行名为 myfirst 的动画,其中设置了所有动画属性:
div
{
animation-name: myfirst;
animation-duration: 5s;
animation-timing-function: linear;
animation-delay: 2s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-play-state: running;
/* Firefox: */
-moz-animation-name: myfirst;
-moz-animation-duration: 5s;
-moz-animation-timing-function: linear;
-moz-animation-delay: 2s;
-moz-animation-iteration-count: infinite;
-moz-animation-direction: alternate;
-moz-animation-play-state: running;
/* Safari 和 Chrome: */
-webkit-animation-name: myfirst;
-webkit-animation-duration: 5s;
-webkit-animation-timing-function: linear;
-webkit-animation-delay: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: alternate;
-webkit-animation-play-state: running;
/* Opera: */
-o-animation-name: myfirst;
-o-animation-duration: 5s;
-o-animation-timing-function: linear;
-o-animation-delay: 2s;
-o-animation-iteration-count: infinite;
-o-animation-direction: alternate;
-o-animation-play-state: running;
}
实例
与上面的动画相同,但是使用了简写的动画 animation 属性:
div
{
animation: myfirst 5s linear 2s infinite alternate;
/* Firefox: */
-moz-animation: myfirst 5s linear 2s infinite alternate;
/* Safari 和 Chrome: */
-webkit-animation: myfirst 5s linear 2s infinite alternate;
/* Opera: */
-o-animation: myfirst 5s linear 2s infinite alternate;
}
html部分
1 <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
2 <select class="professionOne">
3 <option>---请选择职业类别---</option>
4 </select>
5 <select class="professionTwo">
6 <option>---请选择职业---</option>
7 </select>
8 <select class="professionThree">
9 <option>---请选择具体职业---</option>
10 </select>
js部分
1 $(function(){
2 var level1 = [{id:10000,name:‘党的机关、国家机关、群众团体和社会组织、企事业单位负责人‘,pid:10000,lv:1},
3 {id:20000,name:‘专业技术人员‘,pid:20000,lv:1},
4 {id:30000,name:‘办事人员和有关人员‘,pid:30000,lv:1},
5 {id:40000,name:‘社会生产服务和生活服务人员‘,pid:40000,lv:1},
6 {id:50000,name:‘农、林、牧、渔业生产及辅助人员‘,pid:50000,lv:1},
7 {id:60000,name:‘生产制造及有关人员‘,pid:60000,lv:1},
8 {id:70000,name:‘军人‘,pid:70000,lv:1},
9 {id:80000,name:‘不便分类的其他从业人员‘,pid:80000,lv:1},
10 {id:99999,name:‘失业、无业或离退休‘,pid:99999,lv:1}];
11
12 var level2 = [{id:10100,name:‘中国gcd机关负责人‘,pid:10000,lv:2},
13 {id:10200,name:‘国家机关负责人‘,pid:10000,lv:2},
14 {id:10300,name:‘民主党派和工商联负责人‘,pid:10000,lv:2},
15 {id:10400,name:‘人民团体和群众团体、社会组织及其他成员组织负责人‘,pid:10000,lv:2},
16 {id:10500,name:‘基层群众自治组织负责人‘,pid:10000,lv:2},
17 {id:10600,name:‘企事业单位负责人‘,pid:10000,lv:2},
18 {id:20100,name:‘科学研究人员‘,pid:20000,lv:2},
19 {id:20200,name:‘工程技术人员‘,pid:20000,lv:2},
20 {id:20300,name:‘农业技术人员‘,pid:20000,lv:2},
21 {id:20400,name:‘飞机和船舶技术人员‘,pid:20000,lv:2},
22 {id:20500,name:‘卫生专业技术人员‘,pid:20000,lv:2},
23 {id:20600,name:‘经济和金融专业人员‘,pid:20000,lv:2},
24 {id:20700,name:‘法律、社会和宗教专业人员‘,pid:20000,lv:2},
25 {id:20800,name:‘教学人员‘,pid:20000,lv:2},
26 {id:20900,name:‘文学艺术、体育专业人员‘,pid:20000,lv:2},
27 {id:21000,name:‘新闻出版、文化专业人员‘,pid:20000,lv:2},
28 {id:29900,name:‘其他专业技术人员‘,pid:20000,lv:2},
29 {id:30100,name:‘办事人员‘,pid:30000,lv:2},
30 {id:30200,name:‘安全和消防人员‘,pid:30000,lv:2},
31 {id:39900,name:‘其他办事人员和有关人员‘,pid:30000,lv:2},
32 {id:40100,name:‘批发与零售服务人员‘,pid:40000,lv:2},
33 {id:40200,name:‘交通运输、仓储和邮政业服务人员‘,pid:40000,lv:2},
34 {id:40300,name:‘住宿和餐饮服务人员‘,pid:40000,lv:2},
35 {id:40400,name:‘信息传输、软件和信息技术服务人员‘,pid:40000,lv:2},
36 {id:40500,name:‘金融服务人员‘,pid:40000,lv:2},
37 {id:40600,name:‘房地产服务人员‘,pid:40000,lv:2},
38 {id:40700,name:‘祖赁和商务服务人员‘,pid:40000,lv:2},
39 {id:40800,name:‘技术辅助服务人员‘,pid:40000,lv:2},
40 {id:40900,name:‘水利、环境和公共设施管理服务人员‘,pid:40000,lv:2},
41 {id:41000,name:‘居民服务人员‘,pid:40000,lv:2},
42 {id:41100,name:‘电力、燃气及水供应服务人员‘,pid:40000,lv:2},
43 {id:41200,name:‘修理及制作服务人员‘,pid:40000,lv:2},
44 {id:41300,name:‘文化、体育和娱乐服务人员‘,pid:40000,lv:2},
45 {id:41400,name:‘健康服务人员‘,pid:40000,lv:2},
46 {id:49900,name:‘其他社会生产和生活服务人员‘,pid:40000,lv:2},
47 {id:50100,name:‘农业生产人员‘,pid:50000,lv:2},
48 {id:50200,name:‘林业生产人员‘,pid:50000,lv:2},
49 {id:50300,name:‘畜牧业生产人员‘,pid:50000,lv:2},
50 {id:50400,name:‘渔业生产人员‘,pid:50000,lv:2},
51 {id:50500,name:‘农林牧渔生产辅助人员‘,pid:50000,lv:2},
52 {id:59900,name:‘其他农、林、牧、渔业生产及辅助人员‘,pid:50000,lv:2},
53 {id:60100,name:‘农副产品加工人员‘,pid:60000,lv:2},
54 {id:60200,name:‘食品、饮料生产加工人员‘,pid:60000,lv:2},
55 {id:60300,name:‘烟草及其制品加工人员‘,pid:60000,lv:2},
56 {id:60400,name:‘纺织、针织、印染人员‘,pid:60000,lv:2},
57 {id:60500,name:‘纺织品、服装和皮革、毛皮制品加工制作人员‘,pid:60000,lv:2},
58 {id:60600,name:‘木材加工、家具与木制品制作人员‘,pid:60000,lv:2},
59 {id:60700,name:‘纸及纸制品生产加工人员‘,pid:60000,lv:2},
60 {id:60800,name:‘印刷和记录煤介复制人员‘,pid:60000,lv:2},
61 {id:60900,name:‘文教、工美、体育和娱乐用品制作人员‘,pid:60000,lv:2},
62 {id:61000,name:‘石油加工和炼焦、煤化工生产人员‘,pid:60000,lv:2},
63 {id:61100,name:‘化学原料和化学制品制造人员‘,pid:60000,lv:2},
64 {id:61200,name:‘医药制造人员‘,pid:60000,lv:2},
65 {id:61300,name:‘化学纤维制造人员‘,pid:60000,lv:2},
66 {id:61400,name:‘橡胶和塑料制品制造人员‘,pid:60000,lv:2},
67 {id:61500,name:‘非金属矿物制品制造人员‘,pid:60000,lv:2},
68 {id:61600,name:‘采矿人员‘,pid:60000,lv:2},
69 {id:61700,name:‘金属冶炼和压延加工人员‘,pid:60000,lv:2},
70 {id:61800,name:‘机械制造基础加工人员‘,pid:60000,lv:2},
71 {id:61900,name:‘金属制品制造人员‘,pid:60000,lv:2},
72 {id:62000,name:‘通用设备制造人员‘,pid:60000,lv:2},
73 {id:62100,name:‘专用设备制造人员‘,pid:60000,lv:2},
74 {id:62200,name:‘汽车制造人员‘,pid:60000,lv:2},
75 {id:62300,name:‘铁路、船舶、航空设备制造人员‘,pid:60000,lv:2},
76 {id:62400,name:‘电气机械和器材制造人员‘,pid:60000,lv:2},
77 {id:62500,name:‘计算机、通信和其他电子设备制造人员‘,pid:60000,lv:2},
78 {id:62600,name:‘仪器仪表制造人员‘,pid:60000,lv:2},
79 {id:62700,name:‘废弃资源综合利用人员‘,pid:60000,lv:2},
80 {id:62800,name:‘电力、热力、气体、水生产和输配人员‘,pid:60000,lv:2},
81 {id:62900,name:‘建筑施工人员‘,pid:60000,lv:2},
82 {id:63000,name:‘运输设备和通用工程机械操作人员及有关人员‘,pid:60000,lv:2},
83 {id:63100,name:‘生产辅助人员‘,pid:60000,lv:2},
84 {id:69900,name:‘其他生产制造及有关人员‘,pid:60000,lv:2}];
85
86 var level3 = [{id:10201,name:‘国家权力机关负责人‘,pid:10200,lv:3},
87 {id:10202,name:‘国家行政机关负责人‘,pid:10200,lv:3},
88 {id:10203,name:‘人民政协机关负责人‘,pid:10200,lv:3},
89 {id:10204,name:‘人民法院和人民检察院负责人‘,pid:10200,lv:3},
90 {id:10401,name:‘人民团体和群众团体负责人‘,pid:10400,lv:3},
91 {id:10402,name:‘社会团体负责人‘,pid:10400,lv:3},
92 {id:10403,name:‘民办非企业单位负责人‘,pid:10400,lv:3},
93 {id:10404,name:‘社会中介组织负责人‘,pid:10400,lv:3},
94 {id:10405,name:‘基金会负责人‘,pid:10400,lv:3},
95 {id:10406,name:‘宗救组织负责人‘,pid:10400,lv:3},
96 {id:10601,name:‘企业负责人‘,pid:10600,lv:3},
97 {id:10602,name:‘事业单位负责人‘,pid:10600,lv:3},
98 {id:20101,name:‘哲学研究人员‘,pid:20100,lv:3},
99 {id:20102,name:‘经济学研究人员‘,pid:20100,lv:3},
100 {id:20103,name:‘法学研究人员‘,pid:20100,lv:3},
101 {id:20104,name:‘教育学研究人员‘,pid:20100,lv:3},
102 {id:20105,name:‘历史学研究人员‘,pid:20100,lv:3},
103 {id:20107,name:‘农学研究人员‘,pid:20100,lv:3},
104 {id:20108,name:‘医学研究人员‘,pid:20100,lv:3},
105 {id:20109,name:‘管理学研究人员‘,pid:20100,lv:3},
106 {id:20111,name:‘军事学研究人员‘,pid:20100,lv:3},
107 {id:20112,name:‘文学研究人员‘,pid:20100,lv:3},
108 {id:20113,name:‘理学研究人员‘,pid:20100,lv:3},
109 {id:20114,name:‘工学研究人员‘,pid:20100,lv:3},
110 {id:20115,name:‘艺术学研究人员‘,pid:20100,lv:3},
111 {id:20199,name:‘其他科学研究人员‘,pid:20100,lv:3},
112 {id:20201,name:‘地质勘探工程技术人员‘,pid:20200,lv:3},
113 {id:20202,name:‘测绘和地理信息工程技术人员‘,pid:20200,lv:3},
114 {id:20203,name:‘矿山工程技术人员‘,pid:20200,lv:3},
115 {id:20204,name:‘石油天然气工程技术人员‘,pid:20200,lv:3},
116 {id:20205,name:‘冶金工程技术人员‘,pid:20200,lv:3},
117 {id:20206,name:‘化工工程技术人员‘,pid:20200,lv:3},
118 {id:20207,name:‘机械工程技术人员‘,pid:20200,lv:3},
119 {id:20208,name:‘航空工程技术人员‘,pid:20200,lv:3},
120 {id:20209,name:‘电子工程技术人员‘,pid:20200,lv:3},
121 {id:20210,name:‘信息和通信工程技术人员‘,pid:20200,lv:3},
122 {id:20211,name:‘电气工程技术人员‘,pid:20200,lv:3},
123 {id:20212,name:‘电力工程技术人员‘,pid:20200,lv:3},
124 {id:20213,name:‘邮政和快递工程技术人员‘,pid:20200,lv:3},
125 {id:20214,name:‘广播电影电视及演艺设备工程技术人员‘,pid:20200,lv:3},
126 {id:20215,name:‘道路和水上运输工程技术人员‘,pid:20200,lv:3},
127 {id:20216,name:‘民用航空工程技术人员‘,pid:20200,lv:3},
128 {id:20217,name:‘铁道工程技术人员‘,pid:20200,lv:3},
129 {id:20218,name:‘建筑工程技术人员‘,pid:20200,lv:3},
130 {id:20219,name:‘建材工程技术人员‘,pid:20200,lv:3},
131 {id:20220,name:‘林业工程技术人员‘,pid:20200,lv:3},
132 {id:20221,name:‘水利工程技术人员‘,pid:20200,lv:3},
133 {id:20222,name:‘海洋工程技术人员‘,pid:20200,lv:3},
134 {id:20223,name:‘纺织服装工程技术人员‘,pid:20200,lv:3},
135 {id:20224,name:‘食品工程技术人员‘,pid:20200,lv:3},
136 {id:20225,name:‘气象工程技术人员‘,pid:20200,lv:3},
137 {id:20226,name:‘地震工程技术人员‘,pid:20200,lv:3},
138 {id:20227,name:‘环境保护工程技术人员‘,pid:20200,lv:3},
139 {id:20228,name:‘安全工程技术人员‘,pid:20200,lv:3},
140 {id:20229,name:‘标准化、计量、质量和认证认可工程技术人员‘,pid:20200,lv:3},
141 {id:20230,name:‘管理(工业)工程技术人员‘,pid:20200,lv:3},
142 {id:20231,name:‘检验检疫工程技术人员‘,pid:20200,lv:3},
143 {id:20232,name:‘制药工程技术人员‘,pid:20200,lv:3},
144 {id:20233,name:‘印刷复制工程技术人员‘,pid:20200,lv:3},
145 {id:20234,name:‘工业(产品)设计工程技术人员‘,pid:20200,lv:3},
146 {id:20235,name:‘康复辅具工程技术人员‘,pid:20200,lv:3},
147 {id:20236,name:‘轻工工程技术人员‘,pid:20200,lv:3},
148 {id:20237,name:‘土地整治工程技术人员‘,pid:20200,lv:3},
149 {id:20290,name:‘兵器工程技术人员‘,pid:20200,lv:3},
150 {id:20291,name:‘航天工程技术人员‘,pid:20200,lv:3},
151 {id:20299,name:‘其他工程技术人员‘,pid:20200,lv:3},
152 {id:20301,name:‘土壤肥料技术人员‘,pid:20300,lv:3},
153 {id:20302,name:‘农业技术指导人员‘,pid:20300,lv:3},
154 {id:20303,name:‘植物保护技术人员‘,pid:20300,lv:3},
155 {id:20304,name:‘园艺技术人员‘,pid:20300,lv:3},
156 {id:20305,name:‘作物遗传育种栽培技术人员‘,pid:20300,lv:3},
157 {id:20306,name:‘兽医兽药技术人员‘,pid:20300,lv:3},
158 {id:20307,name:‘畜牧与草业技术人员‘,pid:20300,lv:3},
159 {id:20308,name:‘水产技术人员‘,pid:20300,lv:3},
160 {id:20309,name:‘农业工程技术人员‘,pid:20300,lv:3},
161 {id:20399,name:‘其他农业技术人员‘,pid:20300,lv:3},
162 {id:20401,name:‘飞行人员和领航人员‘,pid:20400,lv:3},
163 {id:20402,name:‘船舶指挥和引航人员‘,pid:20400,lv:3},
164 {id:20499,name:‘其他飞机和船舶技术人员‘,pid:20400,lv:3},
165 {id:20501,name:‘西医医师‘,pid:20500,lv:3},
166 {id:20502,name:‘中医医师‘,pid:20500,lv:3},
167 {id:20503,name:‘中西医结合医师‘,pid:20500,lv:3},
168 {id:20504,name:‘民族医医师‘,pid:20500,lv:3},
169 {id:20505,name:‘公共卫生与健康医师‘,pid:20500,lv:3},
170 {id:20506,name:‘药学技术人员‘,pid:20500,lv:3},
171 {id:20507,name:‘医疗卫生技术人员‘,pid:20500,lv:3},
172 {id:20508,name:‘护理人员‘,pid:20500,lv:3},
173 {id:20509,name:‘乡村医生‘,pid:20500,lv:3},
174 {id:20599,name:‘其他卫生专业技术人员‘,pid:20500,lv:3},
175 {id:20601,name:‘经济专业人员‘,pid:20600,lv:3},
176 {id:20602,name:‘统计专业人员‘,pid:20600,lv:3},
177 {id:20603,name:‘会计专业人员‘,pid:20600,lv:3},
178 {id:20604,name:‘审计专业人员‘,pid:20600,lv:3},
179 {id:20605,name:‘税务专业人员‘,pid:20600,lv:3},
180 {id:20606,name:‘评估专业人员‘,pid:20600,lv:3},
181 {id:20607,name:‘商务专业人员‘,pid:20600,lv:3},
182 {id:20608,name:‘人力资源专业人员‘,pid:20600,lv:3},
183 {id:20609,name:‘银行专业人员‘,pid:20600,lv:3},
184 {id:20610,name:‘保险专业人员‘,pid:20600,lv:3},
185 {id:20611,name:‘证券专业人员‘,pid:20600,lv:3},
186 {id:20612,name:‘知识产权专业人员‘,pid:20600,lv:3},
187 {id:20699,name:‘其他经济和金融专业人员‘,pid:20600,lv:3},
188 {id:20701,name:‘法官‘,pid:20700,lv:3},
189 {id:20702,name:‘检察官‘,pid:20700,lv:3},
190 {id:20703,name:‘律师‘,pid:20700,lv:3},
191 {id:20704,name:‘公证员‘,pid:20700,lv:3},
192 {id:20705,name:‘司法鉴定人员‘,pid:20700,lv:3},
193 {id:20706,name:‘审判辅助人员‘,pid:20700,lv:3},
194 {id:20707,name:‘法律顾问‘,pid:20700,lv:3},
195 {id:20708,name:‘宗教教职人员‘,pid:20700,lv:3},
196 {id:20709,name:‘社会工作专业人员‘,pid:20700,lv:3},
197 {id:20799,name:‘其他法律、社会和宗教专业人员‘,pid:20700,lv:3},
198 {id:20801,name:‘高等教育教师‘,pid:20800,lv:3},
199 {id:20802,name:‘中等职业教育教师‘,pid:20800,lv:3},
200 {id:20803,name:‘中小学教育教师‘,pid:20800,lv:3},
201 {id:20804,name:‘幼儿教育教师‘,pid:20800,lv:3},
202 {id:20805,name:‘特殊教育教师‘,pid:20800,lv:3},
203 {id:20899,name:‘其他教学人员‘,pid:20800,lv:3},
204 {id:20901,name:‘文艺创作与编导人员‘,pid:20900,lv:3},
205 {id:20902,name:‘音乐指挥与演员‘,pid:20900,lv:3},
206 {id:20903,name:‘电影电视制作专业人员‘,pid:20900,lv:3},
207 {id:20904,name:‘舞台专业人员‘,pid:20900,lv:3},
208 {id:20905,name:‘美术专业人员‘,pid:20900,lv:3},
209 {id:20906,name:‘工艺美术与创意设计专业人员‘,pid:20900,lv:3},
210 {id:20907,name:‘体育专业人员‘,pid:20900,lv:3},
211 {id:20999,name:‘其他文学艺术、体育专业人员‘,pid:20900,lv:3},
212 {id:21001,name:‘记者‘,pid:21000,lv:3},
213 {id:21002,name:‘编辑‘,pid:21000,lv:3},
214 {id:21003,name:‘校对员‘,pid:21000,lv:3},
215 {id:21004,name:‘播音员及节目主持人‘,pid:21000,lv:3},
216 {id:21005,name:‘翻译人员‘,pid:21000,lv:3},
217 {id:21006,name:‘图书资料与微缩摄影专业人员‘,pid:21000,lv:3},
218 {id:21007,name:‘档案专业人员‘,pid:21000,lv:3},
219 {id:21008,name:‘考古及文物保护专业人员‘,pid:21000,lv:3},
220 {id:21099,name:‘其他新闻出版、文化专业人员‘,pid:21000,lv:3},
221 {id:30101,name:‘行政业务办理人员‘,pid:30100,lv:3},
222 {id:30102,name:‘行政事务处理人员‘,pid:30100,lv:3},
223 {id:30103,name:‘行政执法和仲裁人员‘,pid:30100,lv:3},
224 {id:30199,name:‘其他办事人员‘,pid:30100,lv:3},
225 {id:30201,name:‘人民警察‘,pid:30200,lv:3},
226 {id:30202,name:‘保卫人员‘,pid:30200,lv:3},
227 {id:30203,name:‘消防和应急救援人员‘,pid:30200,lv:3},
228 {id:30299,name:‘其他安全和消防人员‘,pid:30200,lv:3},
229 {id:40101,name:‘采购人员‘,pid:40100,lv:3},
230 {id:40102,name:‘销售人员‘,pid:40100,lv:3},
231 {id:40103,name:‘贸易经纪代理人员‘,pid:40100,lv:3},
232 {id:40104,name:‘再生物资回收人员‘,pid:40100,lv:3},
233 {id:40105,name:‘特殊商品购销人员‘,pid:40100,lv:3},
234 {id:40199,name:‘其他批发与零售服务人员‘,pid:40100,lv:3},
235 {id:40201,name:‘轨道交通运输服务人员‘,pid:40200,lv:3},
236 {id:40202,name:‘道路运输服务人员‘,pid:40200,lv:3},
237 {id:40203,name:‘水上运输服务人员‘,pid:40200,lv:3},
238 {id:40204,name:‘航空运输服务人员‘,pid:40200,lv:3},
239 {id:40205,name:‘装卸搬运和运输代理服务人员‘,pid:40200,lv:3},
240 {id:40206,name:‘仓储人员‘,pid:40200,lv:3},
241 {id:40207,name:‘邮政和快递服务人员‘,pid:40200,lv:3},
242 {id:40299,name:‘其他交通运输、仓储和邮政业服务人员‘,pid:40200,lv:3},
243 {id:40301,name:‘住宿服务人员‘,pid:40300,lv:3},
244 {id:40302,name:‘餐饮服务人员‘,pid:40300,lv:3},
245 {id:40399,name:‘其他住宿和餐饮服务人员‘,pid:40300,lv:3},
246 {id:40401,name:‘信息通信业务人员‘,pid:40400,lv:3},
247 {id:40402,name:‘信息通信网络维护人员‘,pid:40400,lv:3},
248 {id:40403,name:‘广播电视传输服务人员‘,pid:40400,lv:3},
249 {id:40404,name:‘信息通信网络运行管理人员‘,pid:40400,lv:3},
250 {id:40405,name:‘软件和信息技术服务人员‘,pid:40400,lv:3},
251 {id:40499,name:‘其他信息传输、软件和信息技术服务人员‘,pid:40400,lv:3},
252 {id:40501,name:‘银行服务人员‘,pid:40500,lv:3},
253 {id:40502,name:‘证券服务人员‘,pid:40500,lv:3},
254 {id:40503,name:‘期货服务人员‘,pid:40500,lv:3},
255 {id:40504,name:‘保险服务人员‘,pid:40500,lv:3},
256 {id:40505,name:‘典当服务人员‘,pid:40500,lv:3},
257 {id:40506,name:‘信托服务人员‘,pid:40500,lv:3},
258 {id:40599,name:‘其他金融服务人员‘,pid:40500,lv:3},
259 {id:40601,name:‘物业管理服务人员‘,pid:40600,lv:3},
260 {id:40602,name:‘房地产中介服务人员‘,pid:40600,lv:3},
261 {id:40699,name:‘其他房地产服务人员‘,pid:40600,lv:3},
262 {id:40701,name:‘租赁业务人员‘,pid:40700,lv:3},
263 {id:40702,name:‘商务咨询服务人员‘,pid:40700,lv:3},
264 {id:40703,name:‘人力资源服务人员‘,pid:40700,lv:3},
265 {id:40704,name:‘旅游及公共游览场所服务人员‘,pid:40700,lv:3},
266 {id:40705,name:‘安全保护服务人员‘,pid:40700,lv:3},
267 {id:40706,name:‘市场管理服务人员‘,pid:40700,lv:3},
268 {id:40707,name:‘会议及展览服务人员‘,pid:40700,lv:3},
269 {id:40799,name:‘其他租赁和商务服务人员‘,pid:40700,lv:3},
270 {id:40801,name:‘气象服务人员‘,pid:40800,lv:3},
271 {id:40802,name:‘海洋服务人员‘,pid:40800,lv:3},
272 {id:40803,name:‘测绘服务人员‘,pid:40800,lv:3},
273 {id:40804,name:‘地理信息服务人员‘,pid:40800,lv:3},
274 {id:40805,name:‘检验、检测和计量服务人员‘,pid:40800,lv:3},
275 {id:40806,name:‘环境监测服务人员‘,pid:40800,lv:3},
276 {id:40807,name:‘地质勘查人员‘,pid:40800,lv:3},
277 {id:40808,name:‘专业化设计服务人员‘,pid:40800,lv:3},
278 {id:40809,name:‘摄影扩印服务人员‘,pid:40800,lv:3},
279 {id:40899,name:‘其他技术辅助服务人员‘,pid:40800,lv:3},
280 {id:40901,name:‘水利设施管养人员‘,pid:40900,lv:3},
281 {id:40902,name:‘水文服务人员‘,pid:40900,lv:3},
282 {id:40903,name:‘水土保持人员‘,pid:40900,lv:3},
283 {id:40904,name:‘农田灌排人员‘,pid:40900,lv:3},
284 {id:40905,name:‘自然保护区和草地监护人员‘,pid:40900,lv:3},
285 {id:40906,name:‘野生动植物保护人员‘,pid:40900,lv:3},
286 {id:40907,name:‘环境治理服务人员‘,pid:40900,lv:3},
287 {id:40908,name:‘环境卫生服务人员‘,pid:40900,lv:3},
288 {id:40909,name:‘有害生物防制人员‘,pid:40900,lv:3},
289 {id:40910,name:‘绿化与园艺服务人员‘,pid:40900,lv:3},
290 {id:40999,name:‘其他水利、环境和公共设施管理服务人员‘,pid:40900,lv:3},
291 {id:41001,name:‘生活照料服务人员‘,pid:41000,lv:3},
292 {id:41002,name:‘服装裁剪和洗染织补人员‘,pid:41000,lv:3},
293 {id:41003,name:‘美容美发和浴池服务人员‘,pid:41000,lv:3},
294 {id:41004,name:‘保健服务人员‘,pid:41000,lv:3},
295 {id:41005,name:‘婚姻服务人员‘,pid:41000,lv:3},
296 {id:41006,name:‘殡葬服务人员‘,pid:41000,lv:3},
297 {id:41007,name:‘宠物服务人员‘,pid:41000,lv:3},
298 {id:41099,name:‘其他居民服务人员‘,pid:41000,lv:3},
299 {id:41101,name:‘电力供应服务人员‘,pid:41100,lv:3},
300 {id:41102,name:‘燃气供应服务人员‘,pid:41100,lv:3},
301 {id:41103,name:‘水供应服务人员‘,pid:41100,lv:3},
302 {id:41199,name:‘其他电力、燃气及水供应服务人员‘,pid:41100,lv:3},
303 {id:41201,name:‘汽车摩托车修理技术服务人员‘,pid:41200,lv:3},
304 {id:41202,name:‘计算机和办公设备维修人员‘,pid:41200,lv:3},
305 {id:41203,name:‘家用电子电器产品维修人员‘,pid:41200,lv:3},
306 {id:41204,name:‘日用产品修理服务人员‘,pid:41200,lv:3},
307 {id:41205,name:‘乐器维修人员‘,pid:41200,lv:3},
308 {id:41206,name:‘印章制作人员‘,pid:41200,lv:3},
309 {id:41299,name:‘其他修理及制作服务人员‘,pid:41200,lv:3},
310 {id:41301,name:‘群众文化活动服务人员‘,pid:41300,lv:3},
311 {id:41302,name:‘广播、电视、电影和影视录音制作人员‘,pid:41300,lv:3},
312 {id:41303,name:‘文物保护作业人员‘,pid:41300,lv:3},
313 {id:41304,name:‘健身和娱乐场所服务人员‘,pid:41300,lv:3},
314 {id:41305,name:‘文化、娱乐、体育经纪代理人员‘,pid:41300,lv:3},
315 {id:41399,name:‘其他文化、体育和娱乐服务人员‘,pid:41300,lv:3},
316 {id:41401,name:‘医疗辅助服务人员‘,pid:41400,lv:3},
317 {id:41402,name:‘健康咨询服务人员‘,pid:41400,lv:3},
318 {id:41403,name:‘康复矫正服务人员‘,pid:41400,lv:3},
319 {id:41404,name:‘公共卫生辅助服务人员‘,pid:41400,lv:3},
320 {id:41499,name:‘其他健康服务人员‘,pid:41400,lv:3},
321 {id:50101,name:‘作物种子(苗)繁育生产人员‘,pid:50100,lv:3},
322 {id:50102,name:‘农作物生产人员‘,pid:50100,lv:3},
323 {id:50199,name:‘其他农业生产人员‘,pid:50100,lv:3},
324 {id:50201,name:‘林木种苗繁育人员‘,pid:50200,lv:3},
325 {id:50202,name:‘营造林人员‘,pid:50200,lv:3},
326 {id:50203,name:‘森林经营和管护人员‘,pid:50200,lv:3},
327 {id:50204,name:‘木材采运人员‘,pid:50200,lv:3},
328 {id:50299,name:‘其他林业生产人员‘,pid:50200,lv:3},
329 {id:50301,name:‘畜禽种苗繁育人员‘,pid:50300,lv:3},
330 {id:50302,name:‘畜禽饲养人员‘,pid:50300,lv:3},
331 {id:50303,name:‘特种经济动物饲养人员‘,pid:50300,lv:3},
332 {id:50399,name:‘其他畜牧业生产人员‘,pid:50300,lv:3},
333 {id:50401,name:‘水产苗种繁育人员‘,pid:50400,lv:3},
334 {id:50402,name:‘水产养殖人员‘,pid:50400,lv:3},
335 {id:50403,name:‘水产捕捞及有关人员‘,pid:50400,lv:3},
336 {id:50499,name:‘其他渔业生产人员‘,pid:50400,lv:3},
337 {id:50501,name:‘农业生产服务人员‘,pid:50500,lv:3},
338 {id:50502,name:‘动植物疫病防治人员‘,pid:50500,lv:3},
339 {id:50503,name:‘农村能源利用人员‘,pid:50500,lv:3},
340 {id:50504,name:‘农村环境保护人员‘,pid:50500,lv:3},
341 {id:50505,name:‘农机化服务人员‘,pid:50500,lv:3},
342 {id:50506,name:‘农副林特产品初加工人员‘,pid:50500,lv:3},
343 {id:50599,name:‘其他农林牧渔生产辅助人员‘,pid:50500,lv:3},
344 {id:60101,name:‘粮油加工人员‘,pid:60100,lv:3},
345 {id:60102,name:‘饲料加工人员‘,pid:60100,lv:3},
346 {id:60103,name:‘制糖人员‘,pid:60100,lv:3},
347 {id:60104,name:‘畜禽制品加工人员‘,pid:60100,lv:3},
348 {id:60105,name:‘水产品加工人员‘,pid:60100,lv:3},
349 {id:60106,name:‘果蔬和坚果加工人员‘,pid:60100,lv:3},
350 {id:60107,name:‘淀粉和豆制品加工人员‘,pid:60100,lv:3},
351 {id:60199,name:‘其他农副产品加工人员‘,pid:60100,lv:3},
352 {id:60201,name:‘焙烤食品制造人员‘,pid:60200,lv:3},
353 {id:60202,name:‘糖制品加工人员‘,pid:60200,lv:3},
354 {id:60203,name:‘方便食品和罐头食品加工人员‘,pid:60200,lv:3},
355 {id:60204,name:‘乳制品加工人员‘,pid:60200,lv:3},
356 {id:60205,name:‘调味品及食品添加剂制作人员‘,pid:60200,lv:3},
357 {id:60206,name:‘酒、饮料及精制茶制造人员‘,pid:60200,lv:3},
358 {id:60299,name:‘其他食品、饮料生产加工人员‘,pid:60200,lv:3},
359 {id:60301,name:‘烟叶初加工人员‘,pid:60300,lv:3},
360 {id:60302,name:‘烟用材料生产人员‘,pid:60300,lv:3},
361 {id:60303,name:‘烟草制品生产人员‘,pid:60300,lv:3},
362 {id:60399,name:‘其他烟草及其制品加工人员‘,pid:60300,lv:3},
363 {id:60401,name:‘纤维预处理人员‘,pid:60400,lv:3},
364 {id:60402,name:‘纺纱人员‘,pid:60400,lv:3},
365 {id:60403,name:‘织造人员‘,pid:60400,lv:3},
366 {id:60404,name:‘针织人员‘,pid:60400,lv:3},
367 {id:60405,name:‘非织造布制造人员‘,pid:60400,lv:3},
368 {id:60406,name:‘印染人员‘,pid:60400,lv:3},
369 {id:60499,name:‘其他纺织、针织、印染人员‘,pid:60400,lv:3},
370 {id:60501,name:‘纺织品和服装剪裁缝纫人员‘,pid:60500,lv:3},
371 {id:60502,name:‘皮革、毛皮及其制品加工人员‘,pid:60500,lv:3},
372 {id:60503,name:‘羽绒羽毛加工及制品制造人员‘,pid:60500,lv:3},
373 {id:60504,name:‘鞋帽制作人员‘,pid:60500,lv:3},
374 {id:60599,name:‘其他纺织品、服装和皮革、毛皮制品加工制作人员‘,pid:60500,lv:3},
375 {id:60601,name:‘木材加工人员‘,pid:60600,lv:3},
376 {id:60602,name:‘人造板制造人员‘,pid:60600,lv:3},
377 {id:60603,name:‘木制品制造人员‘,pid:60600,lv:3},
378 {id:60604,name:‘家具制造人员‘,pid:60600,lv:3},
379 {id:60699,name:‘其他木材加工、家具与木制品制作人员‘,pid:60600,lv:3},
380 {id:60701,name:‘制浆造纸人员‘,pid:60700,lv:3},
381 {id:60702,name:‘纸制品制作人员‘,pid:60700,lv:3},
382 {id:60799,name:‘其他纸及纸制品生产加工人员‘,pid:60700,lv:3},
383 {id:60801,name:‘印刷人员‘,pid:60800,lv:3},
384 {id:60802,name:‘记录媒介复制人员‘,pid:60800,lv:3},
385 {id:60899,name:‘其他印刷和记录媒介复制人员‘,pid:60800,lv:3},
386 {id:60901,name:‘文教用品制作人员‘,pid:60900,lv:3},
387 {id:60902,name:‘乐器制作人员‘,pid:60900,lv:3},
388 {id:60903,name:‘工艺美术品制作人员‘,pid:60900,lv:3},
389 {id:60904,name:‘体育用品制作人员‘,pid:60900,lv:3},
390 {id:60905,name:‘玩具制作人员‘,pid:60900,lv:3},
391 {id:60999,name:‘其他文教、工美、体育和娱乐用品制作人员‘,pid:60900,lv:3},
392 {id:61001,name:‘石油炼制生产人员‘,pid:61000,lv:3},
393 {id:61002,name:‘炼焦人员‘,pid:61000,lv:3},
394 {id:61003,name:‘煤化工生产人员‘,pid:61000,lv:3},
395 {id:61099,name:‘其他石油加工和炼焦、煤化工生产人员‘,pid:61000,lv:3},
396 {id:61101,name:‘化工产品生产通用工艺人员‘,pid:61100,lv:3},
397 {id:61102,name:‘基础化学原料制造人员‘,pid:61100,lv:3},
398 {id:61103,name:‘化学肥料生产人员‘,pid:61100,lv:3},
399 {id:61104,name:‘农药生产人员‘,pid:61100,lv:3},
400 {id:61105,name:‘涂料、油墨、颜料及类似产品制造人员‘,pid:61100,lv:3},
401 {id:61106,name:‘合成树脂生产人员‘,pid:61100,lv:3},
402 {id:61107,name:‘合成橡胶生产人员‘,pid:61100,lv:3},
403 {id:61108,name:‘专用化学产品生产人员‘,pid:61100,lv:3},
404 {id:61109,name:‘火工品制造、保管、爆破及焰火产品制造人员‘,pid:61100,lv:3},
405 {id:61110,name:‘日用化学品生产人员‘,pid:61100,lv:3},
406 {id:61199,name:‘其他化学原料和化学制品制造人员‘,pid:61100,lv:3},
407 {id:61201,name:‘化学药品原料药制造人员‘,pid:61200,lv:3},
408 {id:61202,name:‘中药饮片加工人员‘,pid:61200,lv:3},
409 {id:61203,name:‘药物制剂人员‘,pid:61200,lv:3},
410 {id:61204,name:‘兽用药品制造人员‘,pid:61200,lv:3},
411 {id:61205,name:‘生物药品制造人员‘,pid:61200,lv:3},
412 {id:61299,name:‘其他医药制造人员‘,pid:61200,lv:3},
413 {id:61301,name:‘化学纤维原料制造人员‘,pid:61300,lv:3},
414 {id:61302,name:‘化学纤维纺丝及后处理人员‘,pid:61300,lv:3},
415 {id:61399,name:‘其他化学纤维制造人员‘,pid:61300,lv:3},
416 {id:61401,name:‘橡胶制品生产人员‘,pid:61400,lv:3},
417 {id:61402,name:‘塑料制品加工人员‘,pid:61400,lv:3},
418 {id:61499,name:‘其他橡胶和塑料制品制造人员‘,pid:61400,lv:3},
419 {id:61501,name:‘水泥、石灰、石膏及其制品制造人员‘,pid:61500,lv:3},
420 {id:61502,name:‘砖瓦石材等建筑材料制造人员‘,pid:61500,lv:3},
421 {id:61503,name:‘玻璃及玻璃制品生产加工人员‘,pid:61500,lv:3},
422 {id:61504,name:‘玻璃纤维及玻璃纤维增强塑料制品制造人员‘,pid:61500,lv:3},
423 {id:61505,name:‘陶瓷制品制造人员‘,pid:61500,lv:3},
424 {id:61506,name:‘耐火材料制品生产人员‘,pid:61500,lv:3},
425 {id:61507,name:‘石墨及炭素制品生产人员‘,pid:61500,lv:3},
426 {id:61508,name:‘高岭土、珍珠岩等非金属矿物加工人员‘,pid:61500,lv:3},
427 {id:61599,name:‘其他非金属矿物制品制造人员‘,pid:61500,lv:3},
428 {id:61601,name:‘矿物采选人员‘,pid:61600,lv:3},
429 {id:61602,name:‘石油和天然气开采与储运人员‘,pid:61600,lv:3},
430 {id:61603,name:‘采盐人员‘,pid:61600,lv:3},
431 {id:61699,name:‘其他采矿人员‘,pid:61600,lv:3},
432 {id:61701,name:‘炼铁人员‘,pid:61700,lv:3},
433 {id:61702,name:‘炼钢人员‘,pid:61700,lv:3},
434 {id:61703,name:‘铸铁管人员‘,pid:61700,lv:3},
435 {id:61704,name:‘铁合金冶炼人员‘,pid:61700,lv:3},
436 {id:61705,name:‘重有色金属冶炼人员‘,pid:61700,lv:3},
437 {id:61706,name:‘轻有色金属冶炼人员‘,pid:61700,lv:3},
438 {id:61707,name:‘稀贵金属冶炼人员‘,pid:61700,lv:3},
439 {id:61708,name:‘半导体材料制备人员‘,pid:61700,lv:3},
440 {id:61709,name:‘金属轧制人员‘,pid:61700,lv:3},
441 {id:61710,name:‘硬质合金生产人员‘,pid:61700,lv:3},
442 {id:61799,name:‘其他金属冶炼和压延加工人员‘,pid:61700,lv:3},
443 {id:61801,name:‘机械冷加工人员‘,pid:61800,lv:3},
444 {id:61802,name:‘机械热加工人员‘,pid:61800,lv:3},
445 {id:61803,name:‘机械表面处理加工人员‘,pid:61800,lv:3},
446 {id:61804,name:‘工装工具制造加工人员‘,pid:61800,lv:3},
447 {id:61899,name:‘其他机械制造基础加工人员‘,pid:61800,lv:3},
448 {id:61901,name:‘五金制品制作装配人员‘,pid:61900,lv:3},
449 {id:61999,name:‘其他金属制品制造人员‘,pid:61900,lv:3},
450 {id:62001,name:‘通用基础件装配制造人员‘,pid:62000,lv:3},
451 {id:62002,name:‘锅炉及原动设备制造人员‘,pid:62000,lv:3},
452 {id:62003,name:‘金属加工机械制造人员‘,pid:62000,lv:3},
453 {id:62004,name:‘物料搬运设备制造人员‘,pid:62000,lv:3},
454 {id:62005,name:‘泵、压缩机、阀门及类似机械制造人员‘,pid:62000,lv:3},
455 {id:62006,name:‘烘炉、水处理、衡器等设备制造人员‘,pid:62000,lv:3},
456 {id:62007,name:‘文化办公机械制造人员‘,pid:62000,lv:3},
457 {id:62099,name:‘其他通用设备制造人员‘,pid:62000,lv:3},
458 {id:62101,name:‘采矿、建筑专用设备制造人员‘,pid:62100,lv:3},
459 {id:62102,name:‘印刷生产专用设备制造人员‘,pid:62100,lv:3},
460 {id:62103,name:‘纺织服装和皮革加工专用设备制造人员‘,pid:62100,lv:3},
461 {id:62104,name:‘电子专用设备装配调试人员‘,pid:62100,lv:3},
462 {id:62105,name:‘农业机械制造人员‘,pid:62100,lv:3},
463 {id:62106,name:‘医疗器械制品和康复辅具生产人员‘,pid:62100,lv:3},
464 {id:62199,name:‘其他专用设备生产制造人员‘,pid:62100,lv:3},
465 {id:62201,name:‘汽车零部件、饰件生产加工人员‘,pid:62200,lv:3},
466 {id:62202,name:‘汽车整车制造人员‘,pid:62200,lv:3},
467 {id:62299,name:‘其他汽车制造人员‘,pid:62200,lv:3},
468 {id:62301,name:‘轨道交通运输设备制造人员‘,pid:62300,lv:3},
469 {id:62302,name:‘船舶制造人员‘,pid:62300,lv:3},
470 {id:62303,name:‘航空产品装配、调试人员‘,pid:62300,lv:3},
471 {id:62304,name:‘摩托车、自行车制造人员‘,pid:62300,lv:3},
472 {id:62399,name:‘其他铁路、船舶、航空设备制造人员‘,pid:62300,lv:3},
473 {id:62401,name:‘电机制造人员‘,pid:62400,lv:3},
474 {id:62402,name:‘输配电及控制设备制造人员‘,pid:62400,lv:3},
475 {id:62403,name:‘电线电缆、光纤光缆及电工器材制造人员‘,pid:62400,lv:3},
476 {id:62404,name:‘电池制造人员‘,pid:62400,lv:3},
477 {id:62405,name:‘家用电力器具制造人员‘,pid:62400,lv:3},
478 {id:62406,name:‘非电力家用器具制造人员‘,pid:62400,lv:3},
479 {id:62407,name:‘照明器具制造人员‘,pid:62400,lv:3},
480 {id:62408,name:‘电气信号设备装置制造人员‘,pid:62400,lv:3},
481 {id:62499,name:‘其他电气机械和器材制造人员‘,pid:62400,lv:3},
482 {id:62501,name:‘电子元件制造人员‘,pid:62500,lv:3},
483 {id:62502,name:‘电子器件制造人员‘,pid:62500,lv:3},
484 {id:62503,name:‘计算机制造人员‘,pid:62500,lv:3},
485 {id:62504,name:‘电子设备装配调试人员‘,pid:62500,lv:3},
486 {id:62599,name:‘其他计算机、通信和其他电子设备制造人员‘,pid:62500,lv:3},
487 {id:62601,name:‘仪器仪表装配人员‘,pid:62600,lv:3},
488 {id:62699,name:‘其他仪器仪表制造人员‘,pid:62600,lv:3},
489 {id:62701,name:‘废料和碎屑加工处理人员‘,pid:62700,lv:3},
490 {id:62799,name:‘其他废弃资源综合利用人员‘,pid:62700,lv:3},
491 {id:62801,name:‘电力、热力生产和供应人员‘,pid:62800,lv:3},
492 {id:62802,name:‘气体生产、处理和输送人员‘,pid:62800,lv:3},
493 {id:62803,name:‘水生产、输排和水处理人员‘,pid:62800,lv:3},
494 {id:62899,name:‘其他电力、热力、气体、水生产和输配人员‘,pid:62800,lv:3},
495 {id:62901,name:‘房屋建筑施工人员‘,pid:62900,lv:3},
496 {id:62902,name:‘土木工程建筑施工人员‘,pid:62900,lv:3},
497 {id:62903,name:‘建筑安装施工人员‘,pid:62900,lv:3},
498 {id:62904,name:‘建筑装饰人员‘,pid:62900,lv:3},
499 {id:62905,name:‘古建筑修建人员‘,pid:62900,lv:3},
500 {id:62999,name:‘其他建筑施工人员‘,pid:62900,lv:3},
501 {id:63001,name:‘专用车辆操作人员‘,pid:63000,lv:3},
502 {id:63002,name:‘轨道交通运输机械设备操作人员‘,pid:63000,lv:3},
503 {id:63003,name:‘民用航空设备操作及有关人员‘,pid:63000,lv:3},
504 {id:63004,name:‘水上运输设备操作及有关人员‘,pid:63000,lv:3},
505 {id:63005,name:‘通用工程机械操作人员‘,pid:63000,lv:3},
506 {id:63099,name:‘其他运输设备和通用工程机械操作人员及有关人员‘,pid:63000,lv:3},
507 {id:63101,name:‘机械设备修理人员‘,pid:63100,lv:3},
508 {id:63102,name:‘船舶、民用航空器修理人员‘,pid:63100,lv:3},
509 {id:63103,name:‘检验试验人员‘,pid:63100,lv:3},
510 {id:63104,name:‘称重计量人员‘,pid:63100,lv:3},
511 {id:63105,name:‘包装人员‘,pid:63100,lv:3},
512 {id:63106,name:‘安全生产管理人员‘,pid:63100,lv:3},
513 {id:63199,name:‘其他生产辅助人员‘,pid:63100,lv:3}];
514
515 var level4 = [{id:‘1A‘,label:‘各类专业、技术人员‘},
516 {id:‘1B‘,label:‘国家机关、党群组织、企事业单位的负责人‘},
517 {id:‘1C‘,label:‘办事人员和有关人员‘},
518 {id:‘1D‘,label:‘商业工作人员‘},
519 {id:‘1E‘,label:‘服务性工作人员‘},
520 {id:‘1F‘,label:‘农林牧渔劳动者‘},
521 {id:‘1G‘,label:‘生产工作,运输工作和部分体力劳动者‘},
522 {id:‘1H‘,label:‘不便分类的其他劳动者‘},
523 {id:‘1I‘,label:‘军人‘}];
524
525 var professionStr1 = ‘<option>---请选择职业类别---</option>‘;
526 var professionStr2 = ‘<option>---请选择职业---</option>‘;
527 var professionStr3 = ‘<option>---请选择具体职业---</option>‘;
528 var professionStrOne = professionStr1;
529 var professionStrTwo = ‘‘;
530 var professionStrThree = ‘‘;
531 for(var i=0;i<level1.length;i++){
532 professionStrOne += ‘<option value="‘ + level1[i].id + ‘">‘ + level1[i].name + ‘</option>‘;
533 }
534 $(".professionOne").html(professionStrOne);
535 $(".professionOne").change(function(){
536 professionStrTwo = professionStr2;
537 if($(this).val() != ‘---请选择职业类别---‘){
538 var professionTwoPid = new Array();
539 for(var j=0;j<level2.length;j++){
540 if (level2[j].pid == $(this).val()) {
541 professionStrTwo += ‘<option value="‘ + level2[j].id + ‘">‘ + level2[j].name + ‘</option>‘;
542 professionTwoPid[j] = level2[j].pid;
543 }
544 }
545 if(professionTwoPid.length == 0){
546 for(var l=0;l<level1.length;l++){
547 if(level1[l].id == $(this).val()){
548 var professionOneValue = level1[l].name;
549 }
550 }
551 professionStrTwo = professionStrThree = ‘<option value="‘ + $(this).val() + ‘">‘ + professionOneValue + ‘</option>‘;
552 $(".professionTwo").html(professionStrTwo);
553 $(".professionThree").html(professionStrThree);
554 }else{
555 $(".professionTwo").html(professionStrTwo);
556 $(".professionThree").html(professionStr3);
557 }
558 }else{
559 $(".professionTwo").html(professionStr2);
560 $(".professionThree").html(professionStr3);
561 }
562 })
563 $(".professionTwo").change(function(){
564 var professionStrThree = professionStr3;
565 if($(this).val() != ‘---请选择职业---‘){
566 var professionThreePid = new Array();
567 for(var k=0;k<level3.length;k++){
568 if (level3[k].pid == $(this).val()) {
569 professionStrThree += ‘<option value="‘ + level3[k].id + ‘">‘ + level3[k].name + ‘</option>‘;
570 professionThreePid[k] = level3[k].pid;
571 }
572 }
573 if(professionThreePid.length == 0){
574 for(var m=0;m<level2.length;m++){
575 if(level2[m].id == $(this).val()){
576 var professionTwoValue = level2[m].name;
577 }
578 }
579 professionStrThree = ‘<option value="‘ + $(this).val() + ‘">‘ + professionTwoValue + ‘</option>‘;
580 $(".professionThree").html(professionStrThree);
581 }else{
582 $(".professionThree").html(professionStrThree);
583 }
584 }else{
585 $(".professionThree").html(professionStr3);
586 }
587 })
588 })
参考文章:
//下载文件
$url = ‘http://xxx.com/99248982.mp3‘;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
$rawdata = curl_exec($ch);
curl_close($ch);
$file_path = dirname(__FILE__) . ‘/temp/‘ . time() . ‘.mp3‘;
$fp = fopen($file_path, ‘w‘);
fwrite($fp, $rawdata);
fclose($fp);
// 上传到upyun
$cdn = uploadToUpyun($file_path, ‘audio/‘ . date(‘Ymd‘), $info[‘uuid‘] . ‘.mp3‘);
经常在个人主页上看到别人的邮箱地址中@被AT符号替代,很是迷惑,这样替代有什么好处呢?还是说html原有的原因使界面中不能出现@,查阅资料后解答如下:
写成AT [at],是为了防止被一些邮件扫描器搜索到,并作为垃圾广告的目标,向其发送垃圾邮件。这是一种自我保护的方式,如果你的垃圾邮件过多,在论坛和网上社区不妨也试试这个方法。
因为垃圾邮件发送者的机器人程序主要是依靠“@”这个符合再连同上下文来判断电子邮件地址的,因此你应该尽量避免使用该符号。例如你可以在网页上写,你的电子邮件是“someoneATsomeplace.com”,使用AT代替@符号,这样只要看到的人都应该会了解你的意思。当然,如果你担心有人不理解,还可以专门在旁边注明,“使用@替换AT”。另外我们还可以通过图片的方式隐藏自己的电子邮件地址。打开一个图形处理软件(例如Windows画图),在里面以文字的方式输入自己的电子邮件地址,然后保存成合适大小的图形文件,这样以后如果需要写出你的电子邮件地址,你只要把这个图片插入进去就可以,人自然能看到你的图片中的内容,不过机器人程序就看不懂了。
<button id="upload">上传图片</button>
<input type="file" name="input_file" id="input_file" />
var filechooser = document.getElementById("input_file");//inupt type="file" 的id
// 用于压缩图片的canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext(‘2d‘);
//瓦片canvas
var tCanvas = document.createElement("canvas");
var tctx = tCanvas.getContext("2d");
var maxsize = 100 * 1024;
//upload 是按钮的id
$("#upload").on("click", function() {
filechooser.click();
})
.on("touchstart", function() {
$(this).addClass("touch")
})
.on("touchend", function() {
$(this).removeClass("touch")
});
var suffix;
filechooser.onchange = function() {
if (!this.files.length) return;
var files = Array.prototype.slice.call(this.files);
var file = files[0];
suffix = file.name.substring(file.name.indexOf("."));
var reader = new FileReader();
//获取图片大小
var size = file.size / 1024 > 1024 ? (~~(10 * file.size / 1024 / 1024)) / 10 + "MB" : ~~(file.size / 1024) + "KB";
reader.onload = function() {
var result = this.result;
console.log(result)
var img = new Image();
img.src = result;
//如果图片大小小于100kb,则直接上传
if (result.length <= maxsize) {
img = null;
upload(result, file.type, $(li));
return;
}
// 图片加载完毕之后进行压缩,然后上传
if (img.complete) {
callback();
} else {
img.onload = callback;
}
function callback() {
var data = compress(img, file.type);
var str = dataURLtoFile(data, ‘tmp‘+suffix);
}
};
reader.readAsDataURL(file);
};
//使用canvas对大图片进行压缩
function compress(img, type) {
var initSize = img.src.length;//应该是图片的整个大小像素或图片大小
var width = img.width;//应该是像素
var height = img.height;//应该是像素
//如果图片大于四百万像素,计算压缩比并将大小压至400万以下
var ratio;
if ((ratio = width * height / 4000000) > 1) {
ratio = Math.sqrt(ratio);
width /= ratio;
height /= ratio;
} else {
ratio = 1;
}
canvas.width = width;
canvas.height = height;
//铺底色
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
//如果图片像素大于100万则使用瓦片绘制
var count;
if ((count = width * height / 1000000) > 1) {
count = ~~(Math.sqrt(count) + 1); //计算要分成多少块瓦片
//计算每块瓦片的宽和高
var nw = ~~(width / count);
var nh = ~~(height / count);
tCanvas.width = nw;
tCanvas.height = nh;
for (var i = 0; i < count; i++) {
for (var j = 0; j < count; j++) {
tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
}
}
} else {
ctx.drawImage(img, 0, 0, width, height);
}
//进行最小压缩
var ndata = canvas.toDataURL(type, 0.1);
console.log(‘压缩前:‘ + initSize);
console.log(‘压缩后:‘ + ndata.length);
console.log(‘压缩率:‘ + ~~(100 * (initSize - ndata.length) / initSize) + "%");
tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
return ndata;
}
//将base64转换为file文件
function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(‘,‘), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {type:mime});
}
1.获得wget https://nodejs.org/dist/v9.3.0/node-v9.3.0.tar.gz 安装包
2.解压:tar -xf node-v9.3.0.tar.gz
3.进入 cd node-v9.3.0
4.执行:https://www.gxlsystem.com/configure && make && make install 进行安装
node -v
报错解决:
做一个软连接即可:
ln -s /srv/tool/node-v9.3.0/node /usr/bin/node
jstat
用于查看服务器上某个服务的GC情况。
一般使用方式或 时间间隔—每个一定时间(指定的时间间隔)输出一次进程pid的内存情况及gc情况。
类加载统计(-class)
Loaded
Bytes
Unloaded
Bytes
Time
加载class的数量
所占用空间大小
未加载数量
未加载占用空间
时间
编译统计(-compiler)
Compiled
Failed
Invalid
Time
FailedType
FailedMethod
编译数量
失败数量
不可用数量
时间
失败类型
失败的方法
JVM编译方法统计(-printcompilation)
参数
注释
Compiled
最近编译方法的数量
Size
最近编译方法的字节码数量
Type
最近编译方法的编译类型
Method
方法名标识
垃圾回收统计(-gc)
参数
注释
S0C
第一个幸存区的大小
S1C
第二个幸存区的大小
S0U
第一个幸存区的使用大小
S1U
第二个幸存区的使用大小
EC
伊甸园区的大小
EU
伊甸园区的使用大小
OC
老年代大小
OU
老年代使用大小
MC
方法区大小
MU
方法区使用大小
CCSC
压缩类空间大小
CCSU
压缩类空间使用大小
YGC
年轻代垃圾回收次数
YGCT
年轻代垃圾回收消耗时间
FGC
老年代垃圾回收次数
FGCT
老年代垃圾回收消耗时间
GCT
垃圾回收消耗总时间
堆内存统计(-gccapacity)
参数
注释
NGCMN
新生代最小容量
NGCMX
新生代最大容量
NGC
当前新生代容量
S0C
第一个幸存区大小
S1C
第二个幸存区的大小
EC
伊甸园区的大小
OGCMN
老年代最小容量
OGCMX
老年代最大容量
OGC
当前老年代大小
OC
当前老年代大小
MCMN
最小元数据容量
MCMX
最大元数据容量
MC
当前元数据空间大小
CCSMN
最小压缩类空间大小
CCSMX
最大压缩类空间大小
CCSC
当前压缩类空间大小
YGC
年轻代gc次数
FGC
老年代GC次数
新生代垃圾回收统计(-gcnew)
参数
注释
S0C
第一个幸存区大小
S1C
第二个幸存区的大小
S0U
第一个幸存区的使用大小
S1U
第二个幸存区的使用大小
TT
对象在新生代存活的次数
MTT
对象在新生代存活的最大次数
DSS
期望的幸存区大小
EC
伊甸园区的大小
EU
伊甸园区的使用大小
YGC
年轻代垃圾回收次数
YGCT
年轻代垃圾回收消耗时间
新生代垃圾回收统计(-gcnewcapacity)
参数
注释
NGCMN
新生代最小容量
NGCMX
新生代最大容量
NGC
当前新生代容量
S0CMX
最大幸存1区大小
S0C
当前幸存1区大小
S1CMX
最大幸存2区大小
S1C
当前幸存2区大小
ECMX
最大伊甸园区大小
EC
当前伊甸园区大小
YGC
年轻代垃圾回收次数
FGC
老年代回收次数
老年代垃圾回收统计(-gcold)
参数
注释
MC
方法区大小
MU
方法区使用大小
CCSC
压缩类空间大小
CCSU
压缩类空间使用大小
OC
老年代大小
OU
老年代使用大小
YGC
年轻代垃圾回收次数
FGC
老年代垃圾回收次数
FGCT
老年代垃圾回收消耗时间
GCT
垃圾回收消耗总时间
老年代内存统计(-gcoldcapacity)
参数
注释
OGCMN
老年代最小容量
OGCMX
老年代最大容量
OGC
当前老年代大小
OC
老年代大小
YGC
年轻代垃圾回收次数
FGC
老年代垃圾回收次数
FGCT
老年代垃圾回收消耗时间
GCT
垃圾回收消耗总时间
元数据空间统计(-gcmetacapacity)
参数
注释
MCMN
最小元数据容量
MCMX
最大元数据容量
MC
当前元数据空间大小
CCSMN
最小压缩类空间大小
CCSMX
最大压缩类空间大小
CCSC
当前压缩类空间大小
YGC
年轻代垃圾回收次数
FGC
老年代垃圾回收次数
FGCT
老年代垃圾回收消耗时间
GCT
垃圾回收消耗总时间
总结垃圾回收统计(-gcutil)
参数
注释
S0
幸存1区当前使用比例
S1
幸存2区当前使用比例
E
伊甸园区使用比例
O
老年代使用比例
M
元数据区使用比例
CCS
压缩使用比例
YGC
年轻代垃圾回收次数
FGC
老年代垃圾回收次数
FGCT
老年代垃圾回收消耗时间
GCT
垃圾回收消耗总时间
又是新的一周了,这周开始一个全新的系列——怎样使用CSS3伪类。 伪类(pseudo class),对于大多数网页设计人员来说,是一个软肋。常常会对伪类产生各种各样的困惑,关于伪类,如果使用得当的话会达到事半功倍的效果。而在最新的CSS3标准中,列出了16种伪类,为我们提供了在新标准中强大的选择方法。 首先还是向大家介绍一下CSS中伪类的历史渊源吧。
伪类简史 CSS1标准完成于1996年,包括了一些直到今天还几乎天天使用的伪类。例如::link:visited:hover:active这些状态可以使用于任意元素,但是通常用于,从此以后便使用伪类这个叫法。你肯定难以想象,这些还早于HTML4标准的制定,而HTML4是在1997年的12月才发布。 CSS2降临 CSS2紧跟着CSS1的步伐,于两年后的1998年5月发布。随着令人期待的position一起出现的伪类有::first-child:lang()
:lang 有多种方法来表示文件的语言,在HTML5中,可以这样定义。当你的站点是随着不同的国家而动态变换语言的时候,现在你可以使用:lang(en)。
:first-child 你可能已经使用过这个选择方法。通常用来添加或者移除一个列表中第一个元素的边框(border),然而在CSS2中却没有与此相对应的:last-child,直到CSS3中这两个兄弟才得以相见。为什么要使用伪类 伪类能够使你的内容风格动态变化,这是使得如此有用的原因之一。在标签中,当用户与链接交互时可以有不同的状态显示。而新的伪类可以使文档内容基于其位置或者状态而动态变换形式。 在 中,一共介绍了16种新的伪类,共分为四种:结构伪类,UI元素状态伪类,目标伪类,否定式伪类。
这就算是本系列的开篇吧,待我下次一一详细介绍这16个新伪类,敬请期待。
环境: CenterOS 7
1.安装nginx之前先安装nginx所需的依赖包
yum -y install zlib zlib-devel openssl openssl-devel pcre pcre-devel
2.使用wget下载nginx压缩文件,并且解压安装,操作步骤如下:
,此处我使用的是1.16.1版本
[root@VM_1_14_centos ~]# cd /data/
[root@VM_1_14_centos data]# wget http://nginx.org/download/nginx-1.15.7.tar.gz
[root@VM_1_14_centos data]# tar -xvf nginx-1.16.1.tar.gz
[root@VM_1_14_centos data]# mkdir //usr/local/nginx -p
[root@VM_1_14_centos data]# ll
total 1008
drwxr-xr-x 9 1001 1001 4096 Dec 17 15:16 nginx-1.15.7
-rw-r--r-- 1 root root 1026732 Nov 27 22:51 nginx-1.15.7.tar.gz
[root@VM_1_14_centos data]# cd nginx-1.15.7/
[root@VM_1_14_centos nginx-1.15.7]# https://www.gxlsystem.com/configure --prefix=/usr/local/nginx
[root@VM_1_14_centos nginx-1.15.7]#make&&make install
3.nginx配置文件 (nginx默认已经配置好了,需要改的话请看下面带‘#‘的描述)
[root@VM_1_14_centos /]# cd /usr/local/nginx/conf/
[root@VM_1_14_centos conf]#
[root@VM_1_14_centos conf]#
[root@VM_1_14_centos conf]#
[root@VM_1_14_centos conf]# vim nginx.conf
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main ‘$remote_addr - $remote_user [$time_local] "$request" ‘
# ‘$status $body_bytes_sent "$http_referer" ‘
# ‘"$http_user_agent" "$http_x_forwarded_for"‘;
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80; #这里需要我们设置web访问的端口
server_name localhost; #这里设置web访问的IP,最终在浏览器访问;默认为localhost 也可以设置成服务器的ip,如129.xxx.xxx.xxx
#charset koi8-r;
#access_log logs/host.access.log main;
location / { #这里配置nginx页面访问跳转
root html; #这里更重要,root是指直接访问 IP:port 时,获取文件的根目录,如果上一行设置为 / ,则直接访问IP:port会去拉取 /html下面的index.html
index index.html index.htm; #接上一行,如果location后面设置了目录 /abc ,则访问IP:port时,会拉取 /html/abc/index.html.
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ .php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ .php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache‘s document root
# concurs with nginx‘s one
#
#location ~ /.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
4.启动nginx服务
[root@VM_1_14_centos sbin]# pwd
/usr/local/nginx/sbin
[root@VM_1_14_centos sbin]# https://www.gxlsystem.com/nginx -t #检查配置文件是否正确无误
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@VM_1_14_centos sbin]# https://www.gxlsystem.com/nginx -c /usr/local/nginx/conf/nginx.conf #使用上一步配置的nginx.conf启动nginx服务
[root@VM_1_14_centos sbin]# ps -aux | grep nginx
root 6341 0.0 0.0 20552 620 ? Ss 16:04 0:00 nginx: master process https://www.gxlsystem.com/nginx -c /usr/local/nginx/conf/nginx.conf
nobody 6342 0.0 0.0 23088 1396 ? S 16:04 0:00 nginx: worker process
root 6360 0.0 0.0 112708 980 pts/1 S+ 16:05 0:00 grep --color=auto nginx
5.打开浏览器,输入服务器上对应的ip地址和端口即可看见nignx默认的页面
这样就配置成功了nginx
有时我们在浏览一个网站时,会发现有些页面的元素是每个页面共有的,比如网页导航、底部注释等。如果每个页面都去重复的写 效率就太低下了,这是就需要继承一个基础的html,然后每个页面只需要在基础的页面上新加元素
例如,这是一个基础的html文件,base.html。他定义了这个网站多个页面的基础样式。我们只需要在这个基础文件中,预留出空间,让调用这个html的文件可以在这个基础上增加样式或者块。
预留CSS样式的格式:{%block css%} 和{%endblock%} 必须是成对出现的,这个中间就是我们空出来的位置,如果有新的css可以写在这里面。
{% block css %}
{# 为新页面预留CSS样式#}
{% endblock %}
预留JS样式格式:原理同上
{% block js %}
{% endblock %}
for 循环逻辑:
{% for nav in daohang %}
{#循环#}
<li><a href="https://www.gxlsystem.com/nav/{{ nav.id }}">{{ nav.name }}</a></li>
{% endfor %}
举例:这个是基础html,可以看到里面空白的block
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>首页_{{ title }} - 一个站在web前端设计之路的女技术员个人博客网站</title>
<meta name="keywords" content="个人博客,杨青个人博客,个人博客模板,杨青" />
<meta name="description" content="{{ title }},是一个站在web前端设计之路的女程序员个人网站,提供个人博客模板免费资源下载的个人原创网站。" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://www.gxlsystem.com/static/css/base.css" rel="stylesheet">
<link href="https://www.gxlsystem.com/static/css/index.css" rel="stylesheet">
<link href="https://www.gxlsystem.com/static/css/m.css" rel="stylesheet">
<script src="https://www.gxlsystem.com/static/js/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" src="https://www.gxlsystem.com/static/js/hc-sticky.js"></script>
<script type="text/javascript" src="https://www.gxlsystem.com/static/js/comm.js"></script>
{% block css %}
{# 为新页面预留CSS样式#}
{% endblock %}
{% block js %}
{% endblock %}
<!--[if lt IE 9]>
<script src="https://www.gxlsystem.com/static/js/modernizr.js"></script>
<![endif]-->
</head>
<body>
<header class="header-navigation" id="header">
<nav><div class="logo"><a href="https://www.gxlsystem.com/">{{ title }}</a></div>
<h2 id="mnavh"><span class="navicon"></span></h2>
<ul id="starlist">
{% for nav in daohang %}
{#循环#}
<li><a href="https://www.gxlsystem.com/nav/{{ nav.id }}">{{ nav.name }}</a></li>
{% endfor %}
</ul>
</nav>
</header>
{% block content %}
#预留空间
{% endblock %}
<footer>
<p>Design by <a href="http://www.besttest.cn" target="_blank">{{ title }}</a> <a href="https://www.gxlsystem.com/">蜀ICP备11002373号-1</a></p>
</footer>
<a href="https://www.gxlsystem.com#" class="cd-top">Top</a>
</body>
</html>
下边这个是继承了基础html的index.html。继承时,要先表明继承的html,{% extends ‘base.html‘ %},然后在base.html中留白的位置可以新增内容。具体如下
{% extends ‘base.html‘ %} #引入继承的文件
{% block content %} #这里是就是加在base文件中 block content 中的部分。
<article>
<aside>
<div class="l_box" id="stickMe">
<div class="about_me">
<h2>关于我</h2>
<ul>
<i><img src="https://www.gxlsystem.com/static/images/4.jpg"></i>
<p><b>杨青</b>,一个80后草根女站长!09年入行。一直潜心研究web前端技术,一边工作一边积累经验,分享一些个人博客模板,以及SEO优化等心得。</p>
</ul>
</div>
<div class="wdxc">
<h2>我的相册</h2>
<ul>
<li><a href="https://www.gxlsystem.com/"><img src="https://www.gxlsystem.com/static/images/7.jpg"></a></li>
<li><a href="https://www.gxlsystem.com/"><img src="https://www.gxlsystem.com/static/images/8.jpg"></a></li>
<li><a href="https://www.gxlsystem.com/"><img src="https://www.gxlsystem.com/static/images/9.jpg"></a></li>
<li><a href="https://www.gxlsystem.com/"><img src="https://www.gxlsystem.com/static/images/10.jpg"></a></li>
<li><a href="https://www.gxlsystem.com/"><img src="https://www.gxlsystem.com/static/images/11.jpg"></a></li>
<li><a href="https://www.gxlsystem.com/"><img src="https://www.gxlsystem.com/static/images/12.jpg"></a></li>
</ul>
</div>
<div class="search">
<form action="/e/search/index.php" method="post" name="searchform" id="searchform">
<input name="keyboard" id="keyboard" class="input_text" value="请输入关键字词" onfocus="if(value==‘请输入关键字词‘){this.style.color=‘#000‘;value=‘‘}" onblur="if(value==‘‘){this.style.color=‘#999‘;value=‘请输入关键字词‘}" type="text">
<input name="show" value="title" type="hidden">
<input name="tempid" value="1" type="hidden">
<input name="tbname" value="news" type="hidden">
<input name="Submit" class="input_submit" value="搜索" type="submit">
</form>
</div>
<div class="fenlei">
<h2>文章分类</h2>
<ul>
<li><a href="https://www.gxlsystem.com/">学无止境(33)</a></li>
<li><a href="https://www.gxlsystem.com/">日记(19)</a></li>
<li><a href="https://www.gxlsystem.com/">慢生活(520)</a></li>
<li><a href="https://www.gxlsystem.com/">美文欣赏(40)</a></li>
</ul>
</div>
<div class="tuijian">
<h2>站长推荐</h2>
<ul>
<li><a href="https://www.gxlsystem.com/">你是什么人便会遇上什么人</a></li>
<li><a href="https://www.gxlsystem.com/">帝国cms 列表页调用子栏目,没有则不显示栏目名称</a></li>
<li><a href="https://www.gxlsystem.com/">第二届 优秀个人博客模板比赛参选活动</a></li>
<li><a href="https://www.gxlsystem.com/">个人博客模板《绅士》后台管理</a></li>
<li><a href="https://www.gxlsystem.com/">你是什么人便会遇上什么人</a></li>
<li><a href="https://www.gxlsystem.com/">帝国cms 列表页调用子栏目,没有则不显示栏目名称</a></li>
<li><a href="https://www.gxlsystem.com/">第二届 优秀个人博客模板比赛参选活动</a></li>
<li><a href="https://www.gxlsystem.com/">个人博客模板《绅士》后台管理</a></li>
</ul>
</div>
<div class="links">
<h2>友情链接</h2>
<ul>
<a href="http://www.yangqq.com">杨青个人博客</a> <a href="http://www.yangqq.com">杨青博客</a>
</ul>
</div>
<div class="guanzhu">
<h2>关注我 么么哒</h2>
<ul>
<img src="https://www.gxlsystem.com/static/images/wx.jpg">
</ul>
</div>
</div>
</aside>
<div class="r_box">
{% for article in articles %}
<li>
<i><a href="https://www.gxlsystem.com/"><img src="https://www.gxlsystem.com/static/{{ article.img }}"></a></i>
<h3><a href="https://www.gxlsystem.com/">{{ article.title }}</a></h3>
<p>{{ article.content }}</p>
</li>
{% endfor %}
</div>
</article>
{% endblock %} #结束后 要endblock,表明填充的内容到这里结束
这样就在base.html的基础上产生了一个新的页面
docker官方镜像为安装php扩展封装了函数,为开发者提供了很大的便利,以下以Dockerfile的形式演示安装gd扩展的方法,安装gd扩展需要安装几个依赖包,安装依赖包使用系统命令,安装命令根据基础镜像的不同有所不同,以下演示两种使用较多的alpine和debian系统的Dockerfile,Dockerfile中同时包含更换国内开源镜像源的内容。
1、基础镜像 php:7.2-fpm-alpine
Dockerfile如下:
FROM php:7.2-fpm-alpine
RUN sed -i ‘s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g‘ /etc/apk/repositories && apk update && apk add libpng-dev freetype-dev libjpeg-turbo-dev && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/ && docker-php-ext-install -j$(nproc) gd
EXPOSE 9000
CMD ["php-fpm"]
2、基础镜像 php:7.2-fpm-stretch (debian 9)
Dockerfile如下:
FROM php:7.2-fpm-stretch
RUN echo "deb http://mirrors.aliyun.com/debian/ stretch main non-free contrib" > /etc/apt/sources.list && echo "deb-src http://mirrors.aliyun.com/debian/ stretch main non-free contrib" >> /etc/apt/sources.list && echo "deb http://mirrors.aliyun.com/debian-security stretch/updates main" >> /etc/apt/sources.list && echo "deb-src http://mirrors.aliyun.com/debian-security stretch/updates main" >> /etc/apt/sources.list && echo "deb http://mirrors.aliyun.com/debian/ stretch-updates main non-free contrib" >> /etc/apt/sources.list && echo "deb-src http://mirrors.aliyun.com/debian/ stretch-updates main non-free contrib" >> /etc/apt/sources.list && echo "deb http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib" >> /etc/apt/sources.list && echo "deb-src http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib" >> /etc/apt/sources.list && rm -f /etc/apt/sources.list.d/* && apt-get update && apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng-dev && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include && docker-php-ext-install -j$(nproc) gd
EXPOSE 9000
CMD ["php-fpm"]
XHLHttpRequest对象
==ajax技术能够向服务器请求额外的数据而无需卸载页面==。其核心技术是XMLHttpRequest对象(XHR)。
IE7之前的旧版本通过MSXML库中的ActiveX对象实现。而IE7及之后的浏览器支持XMLHttpRequest构造函数
可以做兼容性的createXHR();
这个例子先检查XHR对象是否存在,存在就返回XHR对象,不存在就检查ActiveX对象是否存在,存在就返回,如果两个对象都不存在,就返回错误。
XHR的使用
XHR有一个方法叫open(),接收三个参数,请求方式(get,post),请求的url,表示是否异步请求的布尔值(true为异步)
==open()方法不会直接发送请求,而是启用一个请求以备发送==
XHR还有一个方法叫send(),接收一个参数,即作为请求主体要发送的数据。
==send()和open()配合使用就可以发送特定的请求==。
上面代码是同步执行的,所以要等服务器响应请求后js代码才会继续执行。服务器响应后会自动为XHR对象填充以下信息:
属性
描述
responseText
作为响应主题被返回的文本
responseXML
如果响应的内容是"text/xml"或"application/xml",这个属性将保存响应数据的XML DOM文档 ,如果不是xml文件,这个值为空
status
HTTP的响应状态
statusText
HTTP响应状态说明
状态判断
尽量不要用statusText去判断,statusText在跨域时不太可靠。
注意:浏览器有时候会错误的报告204状态,IE的XHR的ActiveX会将201设置为1223,IE中原生的XHR会将204规范为200,而opera或将status设置为0。
请求/响应的活动阶段
可以检测XHR对象的readyState属性来求得请求/响应的当前阶段
0:未开始,还没有调用open()方法。
1:启用,调用open()方法
2:发送,调用send()方法
3:接收,服务器接收到部分请求
4:完成,服务器接收到所有请求,而且客户端可以使用了。
这些阶段的改变会触发readystatechange事件,可以根据该事件来检测readyState
可以用xhr.abort()在响应前取消异步请求,
这个方法会使XHR停止触发时间,再也不允许访问与响应有关的对象属性,这个方法在请求终止后还会对XHR解引,不建议XHR重用,会影响内存。
请求头
不同浏览器发送的信息可能有所不同
设置:setRequestHeader()方法可以自定义头部信息,接收两个参数头部字段的名称和头部字段的值,但是必须在open()与send()之间调用
响应头
获取:getResponseHeader(),传入头部字段的名称,获得相应的响应头信息
获取全部:getAllResponseHeaders(),取得一个包含所有头部信息的长字符串
Get请求
get请求常用于向服务器查询某些信息。
GET请求经常发生的错误是查询字符串的格式问题,查询字符串中的每个参数名和值都需要encodeURIComponent()来先进行编码。
使用这个函数可以保证查询字符串的格式良好,能可靠的用于XHR对象
POST请求
post请求用于向服务器发送应该被保存的数据,把数据作为请求主体提交。
post请求可以提交XML文档
默认情况下,服务器对post请求和Web表单提交不会一视同仁。如果需要提交表单,可以使用如下代码
/**
* 排序汇总
* */
var result = Enumerable.From(vm.productList).GroupBy("$.goods_id", null,
function (key, g) {
var result = {
currency: key,
total: g.Sum(c => parseInt(c.num == " " ? 0 : c.num))
}
return result;
}).ToArray();
/**
* FirstOrDefault 获取第一个
* */
vm.productList.forEach(function (item) {
var arrRes = Enumerable.From(result).FirstOrDefault(-1,x => x.currency == item.goods_id);
if (arrRes != "" && arrRes != null && arrRes != undefined && arrRes.currency == item.goods_id) {
item.FOrderByNum = parseInt(arrRes.total) ;
}
})
//排序
var productList = Enumerable.From(vm.productList).OrderByDescending("x=>x.FOrderByNum").ToArray();//降序OrderByDescending()
最后实现的效果就是:按照最大的数量从上往下排序
1 <!DOCTYPE html>
2 <html lang="en">
3
4 <head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <meta http-equiv="X-UA-Compatible" content="ie=edge">
8 <title>多媒体元素</title>
9 </head>
10
11 <body>
12 <img src="https://www.gxlsystem.com/images/accept.png" alt="确认">
13 <br>
14 <img src="https://www.gxlsystem.com/images/apple.png" alt="苹果">
15
16 <a href="http://edu.51cto.com" target="_blank">打开页面</a>
17
18 <hr>
19
20 <!-- 图片链接 -->
21 <a href="http://edu.51cto.com" target="_blank">
22 <img src="https://www.gxlsystem.com/images/accept.png" alt="确认打开">
23
24 </a>
25 <hr>
26
27 <!-- 加载视频 -->
28 <video src="https://www.gxlsystem.com/images/timessquare.webm" width="360" height="240" controls autoplay muted></video>
29
30 <h2>preload预加载并指定画面</h2>
31 <video src="https://www.gxlsystem.com/images/1-6.mp4" width="360" height="240" controls preload="metadata" poster="images/2019.png"></video>
32
33 <!-- 兼容性问题 -->
34 <video width="360" height="240" controls preload="metadata" poster="images/2019.png">
35 <source src="https://www.gxlsystem.com/images/timessquare.webm" type="video/webm">
36 <source src="https://www.gxlsystem.com/images/timessquare.ogv" type="video/ogv">
37 <source src="https://www.gxlsystem.com/images/timessquare.mp4" type="video/mp4">
38 </video>
39
40 </body>
41
42 </html>
jsoup爬取文章内容
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//response.getWriter().append("Served at: ").append(request.getContextPath());
String agent1 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36";
int pageNum=1;
int pageSize=899;
//for(pageNum=1;pageNum<101;pageNum++)
for(pageNum=1;pageNum<2;pageNum++)
{
try {
int page1= 277;
Map<Integer,String> map1 = ManageMySQL.getNewsLinkInTable(page1,pageSize,"data_szyjglj");
for(Integer key : map1.keySet())
{
System.out.println(key+" "+map1.get(key));
String news_link = map1.get(key);
String context1="";
String source1="";
//String context1 = getContentByURL(news_link).replace(" ", "");
Document documentRoot = Jsoup.connect(news_link).userAgent(agent1).get();
Elements elements1 = documentRoot.select("div.source span");
if(elements1.size()==2)
{
Element span_ele = elements1.get(0);
source1 = span_ele.text();
}
Elements elements2 = documentRoot.select("div.view_box");
if(elements2.size()==1)
{
Element div_ele = elements2.get(0);
context1 = div_ele.text();
}
ManageMySQL.updateContextAndPublishDate(key, context1.replace("‘", "").replace(""", ""),source1,"data_szyjglj");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Web高效开发必备的PHP框架项目名称:多功能THinkPHP开源框架项目名称:基于Laravel的轻量级web部署系统Fixhub
1、项目名称:多功能 THinkPHP 开源框架
项目简介:使用 THinkPHP 开发项目的过程中把一些常用的功能或者第三方 sdk 整合好,开源供亲们参考,如 Auth 权限管理、支付宝、微信支付、阿里oss、友盟推送、融云即时通讯、云通讯短信、Email、Excel、PDF 等等。这些都是经过线上运营考验的,无毒害可以免费放心折腾使用。只要不会某一天找到我说因为借鉴了这个开源产品的一些方法,而导致了好几个亿的损失,要我负责并赔偿就好~  ̄へ ̄
此开源产品面向所有 THinkPHP 开发用户,因为我的基本思想是不改动框架的源文件,只是围绕着框架扩展,所以整合的这些功能都可以无痛兼容各种环境,既可以按照文章将整合的这些功能方便的移植到你已有的项目中,也可以直接以此项目为基础开发。
2、项目名称:基于 Laravel 的轻量级 web 部署系统 Fixhub
项目简介:Fixhub 是一款免费、开源,基于 Laravel 5.3 框架开发的 web 自动部署系统。目前实现的功能主要包括:
支持 PHP、Python、JAVA、Ruby 等项目的发布。
通过 SSH 将程序部署到多台服务器上。
直接从 Git 仓库克隆项目代码并进行打包、安装。
执行远程服务器 bash 命令。
通过 Websocket 实现项目部署状态的实时跟踪。
在服务器保留追溯版本记录,以便快速回滚。
通过任务计划进行项目健康检测。
可通过 webhook 触发部署。
上线单申请、审核流程。
Slack 和邮件通知。
3、项目名称: 基于Yii 框架协同办公管理系统 IBOS
项目简介:IBOS 是一个基于PHP开发、Yii框架、免费开源的,快速、高效的协同办公管理系统。最新的 IBOS 为协同办公应用开发提供了强有力的支持,这些支持包括:
复杂的用户组织结构管理支持。
灵活和完善的角色权限控制体系,权限粒度支持到方法的权限设置。
实用的功能和完善的插件机制。
模块化的功能应用机制,可单独安装卸载单个应用。
云平台支持提供了对新浪 SAE 平台和本地环境双重支持的选择,具备“横跨性”和“平滑性”,支持本地化开发和调试以及部署切换。
缓存支持提供了包括文件、数据库、Memcache、Xcache、Redis 等多种类型的缓存支持。
4、项目名称:基于 CodeIgniter 框架的 CMS
项目简介:菜鸟 CMS 是用 CodeIgniter + bootstrap2.0 面向开发人员的通用管理后台,视情况而定是否运用到你自己的项目中,其主要功能包括:
权限模块
会员模块
文章模块
幻灯片模块
类别模块
缓存模块
日志模块
系统设置
链接: 提取码:x2p5
免费分享,但是X度限制严重,如若链接失效点击链接或搜索加群 群号。
5、项目名称:基于 zend 开发的 CMF 系统
项目简介:本项目是一个基于 zend framework 1.12.3 开发的 CMF 系统。
在不改动 zend framework 代码的基础上构建的基于 app 的 CMF 系统。
自带后台,以及安装模块。
对 zend framework 的 ini 配置文件进行缓存,大大提高了 zend framework 的运行速度。
巧妙的使用 zend framework 的 layout ,view 模块,使各 app 模块,可以使用全局 layout 进行布局,也可以 app 模块自定义的布局,(默认情况下 app 模块未定义 layout 布局将使用全局布局)。
数据库通过定义可以开启读写分离功能,通过配置 ini 文件,可以很方便的分库,各分库之间的功能可以无缝调用。
每个 app 模块包含有一个 library 库,各 app 模块 libray 库,可以互相调用,libray 分 dao,service,tool 三层结构,非常方便以后扩展。
类库实现自动加载,不需要特意 include。
等等还有许多其他功能,适合初学 zend framework 的开发人员学习以及高级开发人员在项目中使用。
6、项目名称:基于 phalcon 开发的内容管理系统
项目简介:本项目是基于 Phalcon 开发的内容管理系统。 特性:
继承 Phalcon 框架全功能。
多网站支持,异站点用户文件、同网站私有/共有网站隔离。
强大的个性化环境,每个用户可以对网站内容和表现形式进行个性化设置。
基于用户角色、模块、角色的权限控制系统,当然,您也可以通过回调函数进行更精细控制。
提供的站内搜索系统原生支持全文搜索。
使用 volt 编写主题模板,类 twig 语法,单比 twig 更高效。
Tolowan 提供的实体管理、字段管理、表单管理、模型管理等机制,可以大大缩减二次开发的难度和所需时间。
nginx 配置转向
location / {
proxy_pass http://localhost:8080;
try_files $uri $uri/ =404;
}
php 虚拟环境
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/erudite_valley/public
ServerName localhost
#LogLevel info ssl:warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
#Include conf-available/serve-cgi-bin.conf
<Directory /var/www/erudite_valley/public>
#Options FollowSymLinks
#Options Indexes FollowSymLinks MultiViews
#AllowOverride None
DirectoryIndex index.html index.htm index.php
options FollowSymLinks
#DirectoryIndex index.php index.html server.php
Allow from all
AcceptPathInfo On
AllowOverride All
</Directory>
</VirtualHost>
1、PHP支持多继承吗?
不支持。PHP中只允许单继承,父类可以被一个子类用关键字“extends”继承。
2、PHP如何实现多继承吗?
可以使用 interface 或 trait 实现 。
1.使用 interface 声明类不能被实例化,并且属性必须是常量,方法不能有方法体
2.trait 声明的类不能被实例化,由use引入,会覆盖父类的相同属性及方法,如果有多个use,那么按顺序下面的覆盖最上面的相同的属性及方法
<div class="datetext">
<img class="dateLeft" src="https://www.gxlsystem.com/images/dateLeft.png">
<div><span class="startDate">2019-07-08</span> 至 <span class="endDate">2019-07-14</span></div>
<img class="dateRight" bindtap="nextWeek" src="https://www.gxlsystem.com/images/dateRight.png">
</div>
// 一周时间表
function getBeforeDate(n) {//n为你要传入的参数,当前为0,前一天为-1,后一天为1
var date = new Date();
var year, month, day;
date.setDate(date.getDate() + n);
year = date.getFullYear();
month = date.getMonth() + 1;
day = date.getDate();
s = year + ‘-‘ + (month < 10 ? (‘0‘ + month) : month) + ‘-‘ + (day < 10 ? (‘0‘ + day) : day);
return s;
}
getBeforeDate(0);
function addDate(date, days) {
var d = new Date(date);
d.setDate(d.getDate() + days);
var month = d.getMonth() + 1;
var day = d.getDate();
if (month < 10) {
month = "0" + month;
}
if (day < 10) {
day = "0" + day;
}
var val = d.getFullYear() + "-" + month + "-" + day;
return val;
}
var nowDate = new Date();
//当前日
var nowDay = nowDate.getDate();
//今天是本周的第几天
var nowDayOfWeek = nowDate.getDay();
// console.log(nowDayOfWeek)
// console.log(getBeforeDate(1 - nowDayOfWeek));
// 获取当前一周时间表
$(‘.startDate‘).html(getBeforeDate(1 - nowDayOfWeek));
$(‘.endDate‘).html(getBeforeDate(1 - nowDayOfWeek + 6));
// 点击上一周
$(‘.dateLeft‘).on(‘click‘, function () {
let dateStart = addDate($(‘.startDate‘).html(), -7);
let dateEnd = addDate($(‘.endDate‘).html(), -7);
$(‘.startDate‘).html(dateStart);
$(‘.endDate‘).html(dateEnd);
})
// 点击下一周
$(‘.dateRight‘).on(‘click‘, function () {
let dateStart = addDate($(‘.startDate‘).html(), 7);
let dateEnd = addDate($(‘.endDate‘).html(), 7);
$(‘.startDate‘).html(dateStart);
$(‘.endDate‘).html(dateEnd);
})
.datetext {
padding: 0 0.4267rem;
height: 1.3067rem;
display: flex;
align-items: center;
background: #fff;
margin-top: 0.0133rem;
}
.datetext>img {
width: 0.6667rem;
height: 0.6667rem;
}
.datetext>div {
flex: 1;
text-align: center;
color: #333;
font-size: 0.4533rem;
}
1. npm install -g @angular/cli
2. 新建项目 ng new my-app
3. 定位至项目路径下 cd my-app
4. 启动项目 ng serve --open 浏览器看到vue主页说明项目启动成功
5. 安装bootstrap和jquery :
npm install --save jquery
npm install bootstrap --save
6. 在 .angular-cli.json文件中进行配置(路径看项目实际路径)
在styles中添加 "/node_modules/bootstrap/dist/css/bootstrap.min.css"
在scripts中添加 "/node_modules/bootstrap/dist/js/bootstrap.min.js"
"/node_modules/jquery/dist/jquery.js"
7. 重新启动项目
1、
2、测试代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript" >
<!--
window.onload = function()
{
// var strTest = "1234567";
// strTest = strTest.substr(2);
// console.log("strTest : " + strTest);
// var str ="GET / HTTP/1.1
"
// +"Host: www.google.com
"
// +"User-Agent: curl/7.43.0
"
// +"Accept: */*
"
// +"
";
var str ="GET / HTTP/1.1
"
+"Host: www.baidu.com
"
+"User-Agent: curl/7.43.0
"
+"Accept: */*
"
+"
";
var strHex = strToHexCharCode(str);
console.log(str);
console.log("字符串 转为 16进制数值的字符串:
");
console.log(strHex);
console.log("");
// strHex = "474554202f20485454502f312e310d0a486f73743a"
// +"207777772e676f6f676c652e636f6d0d0a55736572"
// +"2d4167656e743a206375726c2f372e34332e300d0a"
// +"4163636570743a202a2f2a0d0a0d0a";
strHex = "474554202f20485454502f312e310d0a486f73743a207777772e62616964752e636f6d0d0a557365722d4167656e743a206375726c2f372e34332e300d0a4163636570743a202a2f2a0d0a0d0a";
console.log(strHex);
console.log("16进制数值的字符串 转为 字符串:
");
str = hexCharCodeToStr(strHex);
console.log(str);
};
//字符串转16进制
function strToHexCharCode(_str)
{
if(_str === "")
return "";
var hexCharCode = [];
hexCharCode.push("0x
");
for(var i = 0; i < _str.length; i++) {
var str = _str.charCodeAt(i).toString(16);
str = LeftPad_2(str);
hexCharCode.push(str);
}
return hexCharCode.join("");
}
// 16进制转字符串
function hexCharCodeToStr(_hexCharCodeStr)
{
var trimedStr = _hexCharCodeStr.trim();
var rawStr = trimedStr.substr(0,2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr;
var len = rawStr.length;
if(len % 2 !== 0) {
alert("Illegal Format ASCII Code!");
return "";
}
var curCharCode;
var resultStr = [];
for(var i = 0; i < len;i = i + 2) {
curCharCode = parseInt(rawStr.substr(i, 2), 16); // ASCII Code Value
resultStr.push(String.fromCharCode(curCharCode));
}
return resultStr.join("");
}
function LeftPad_2(_str)
{
if (_str.length == 1)
return "0"+_str;
return _str;
}
-->
</script>
</head>
<body>
</body>
</html>
3、
4、
5、
1 import keras
2 from keras.models import Sequential
3 from keras.layers import Input,Dense,Activation,Conv2D,MaxPooling2D,Flatten
4 from keras.datasets import mnist
5
6
7 (x_train,y_train),(x_test,y_test) = mnist.load_data()
8 x_train = x_train.reshape(-1, 28, 28, 1) #######
9 x_train = x_train.astype("float32")
10 print(x_train.shape)
11 y_train = y_train.astype("float32")
12 x_test = x_test.reshape(-1,28,28,1)
13 x_test = x_test.astype("float32")
14 y_test = y_test.astype("float32")
15
16 print(y_train)
17 x_train /= 255
18 x_test /= 255
19
20 from keras.utils import np_utils
21 y_train_new = np_utils.to_categorical(num_classes=10,y=y_train)
22 print(y_train_new)
23 y_test_new = np_utils.to_categorical(num_classes=10,y=y_test)
24
25 def LeNet_5():
26 model = Sequential()
27 model.add(Conv2D(filters=6,kernel_size=(5,5),padding="valid",activation="tanh",input_shape=[28, 28, 1]))
28 model.add(MaxPooling2D(pool_size=(2,2)))
29 model.add(Conv2D(filters=16,kernel_size=(5,5),padding="valid",activation="tanh"))
30 model.add(MaxPooling2D(pool_size=(2,2)))
31 model.add(Flatten())
32 model.add(Dense(120,activation="tanh"))
33 model.add(Dense(84,activation="tanh"))
34 model.add(Dense(10,activation="softmax"))
35 return model
36
37 def train_model():
38 model = LeNet_5()
39 model.compile(optimizer="adam",loss="categorical_crossentropy",metrics=["accuracy"])
40 model.fit(x_train,y_train_new,batch_size=64,epochs=1,verbose=1,validation_split=0.2,shuffle=True)
41 return model
42
43 model = train_model()
44
45 loss,accuracy = model.evaluate(x_test,y_test_new)
46 print(loss,accuracy)
前言
(在过去)对我这种渣渣来说,在项目里面一旦配置好了webpack之后,就再也不想碰这玩意儿了,因为实在是太多的坑。。。使用一个插件可能要把config文件改个十几二十遍,还得不断地跑起来看是不是生效了,有时候突然生效了也不知道原因是啥-.-反正下次再配的时候又会不记得。。
笔者对webpack是又爱又恨啊(应该很多读者也有一样的想法吧hhhh)又不得不去使用它,所以在前端的学习路上一路与webpack斗智斗勇,因而有了这篇wbpack的学习笔记,以下是笔者在实践中还有参考了无数优秀的webpack相关文章写下的笔记,假如有不当之处请及时指出~谢谢????
webpack是?了解一下
webpack可以说是一个模块打包器,让你可以放心地写你的前端项目,放心地使用社区上最流行的库,模块,插件,框架等等。不用担心,webapck都会根据你的项目帮助你将其打包好,打包好的文件可以直接提供给浏览器使用噢。
四个核心
入口(entry)
我们要告诉webpack它应该从哪里开始构建我们的项目,所以我们要指定一个或多个js文件作为入口文件(parcel是可以以html和js作为入口文件),然后webpack就会从入口文件开始构建其内部依赖,形成一个依赖图。
出口(output)
webpack构建好后会生成称为的文件中,所以我们需要告诉webpack出口文件放在哪个位置,如何命名等规则。
预处理器(loader)
因为webpack本身只认识JavaScript文件,loader可以帮我们把其他类型的文件转化为它能处理的模块,包括图片,字体,css,ES6,ts,vue模版等。不同的文件会有不同的loader去转化。当然你可以在每个loader按照粒度去配置你的逻辑,但是要注意书写顺序等小问题噢(也就是说会有很多各种各样的小坑等着你)
插件(plugins)
都说插件是webpack的支柱功能,它负责完成loader无法完成的其他事项。我们可以在插件里引入webpack自带的一些优化插件,例如,等,还有一些需要我们自己install的插件(也属于webpack生态圈中的),例如,等(更多可以看看官网的介绍 ),还有就是一些webpack社区中一些优秀的插件例如,等。。。可能对一些刚刚入门的同学来说有点看不懂,不过没所谓,慢慢来,你以后都会用上的~
以下是一个简单的栗子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
引人注目的几大特性
tree shaking
大概意思就是自动剔除一些我们程序中不会用上的多余代码,而webpack可以结合一些能够删除未引用代码的插件(例如uglifyJS),帮助我们完成这个事情。例如你import了一个js文件但是只用了里面的其中一个函数,webpack打包的时候就会只打包那个函数的代码而不是整个js文件。
code split
webpack能够按需对代码进行分离,还能动态的并行的去加载这些文件,(合理应用的情况下)这能让我们大大提高加载效率。我们可以通过入口起点的直接分离,动态引入模块,提取公共模块等方式进行代码分离~
开发环境
我们在开发的时候,出现bug时当然是不想看着打包后的代码来调试的(而且不一定看得懂),我们希望的是我们能在本地起一个服务,直接调试自己的源代码,明确追踪到每一行代码,而且能做到改完代码保存后能马上看到效果,而不是要刷新整个页面或者整个项目跑一遍,而webpack可以帮我们做到这一点。
构建&执行过程
要完全理解webpack整个构建过程是有难度的,这需要你去学习webpack的源码,这里简单地说一下webpack的执行过程:
webpack先识别我们写的配置表选项,进行一系列的初始化
webpack从入口文件开始进行编译,递归解析对应依赖的文件,需要进行预处理的文件会根据配置的loader去转换
根据不同块之间的依赖进行分组,分成不同的
将不同的转成对应的输出文件
plugin会贯穿这个编译的过程,会在适当的时机调用适当的插件
对于详细地结合源码分析,日后会继续和大家学习与分享~
作为一个程序猿(又或者是程序媛),出来闯荡江湖,没有几种必杀技在手,那是肯定无法赢得江湖名声的,除了必杀技之外,武器也是很重要的,但是一把青钢剑,肯定是无法赢得万千少男少女的欢心的。就连小李飞刀,人家也没记住他的帅,只是记得了他的飞刀。
难道不是吗?
本文着重介绍对于程序猿来说,居家旅行编程泡妞的七种必备武器,以及若干实(zhuang)用(bi)必选。
开发环境
最隆重推荐:XAMPP
有win版本、mac版本、linux版本等等,全部都齐备了。标准的英语定义是:XAMPP is an easy to install Apache distribution containing MySQL, PHP, and Perl.
大家看英文就知道是包括什么东西了,一次过下完,基本上该配置好的东西全部都配置好了。很多公司的生产环境一样使用这个东西。
备选方案:WAMPSERVER,a Windows web development environment.
这个是一个备选的建议,不过如果可以,还是都选择XAMPP吧。
IDE编辑器
据说真正的神人,是不用集成IDE的,他们喜欢VI、VIM和NOTEPAD,越简单越好,因为越简单逼格越高。
不过,我们今天不是来装逼的,我们今天是来讲实用的。
当之无愧,第一名推荐:PHPStorm,当前最新版本在8.1之后了。是一个俄罗斯公司的产品, JetBrains旗下还有一个叫Webstorm的,也是超级牛逼的产品。让风暴来得更猛烈一下吧!
推荐理由:方便、专业化,基本上别人能做的,他都能做,包括编程、调试等,还集成了SVN管理和自动上传到网站服务器的功能等等。光说,肯定说不完。
需要说明的是,这是一个商业软件,也就是需要人民币的。什么,你没破解过软件……是真的程序员吗?
另外一个隆重推荐,当然是Sublime Text了,和PHPStorm一样,Mac平台和Win平台都有版本。一样有PHPStorm的良好的编辑功能,关键还没有那么臃肿,非常轻便 Slim。如果是phpstorm是这个分类里面的史泰龙,非常强大无所不包,那Sublime Text就是一个轻盈斯文的小家碧玉了。
看大家口味吧。
也别问我网址,不会问度娘的程序员,我觉得根本没法做一个合格的程序员。
数据库工具
数据库工具,实在是太多了。我自己用过mac平台和win平台的工具,发现其实差别还是挺大的。
首先介绍一下第三名:MysqlWorkbench;出身高贵,就是mysql官方出品的,功能很好很强大,但是对中文支持一般,缺点是特别的臃肿,我也不知道为是那么要做一个那么庞大的工具。
最牛逼的地方,我们是用来做数据库设计的,有很好的支持。就是打开EER Diagram那个界面时候用的。强烈推荐哦。
另外,介绍Mac平台下专用的,Sequal Pro,当前版本1.0,新出的,很好奇我为什么推荐这个吧。因为的确是很好用,我在mac下面试过几个,觉得这个实在不错。
唯一的遗憾是,好像只有英文版本的。拼音好,但是英文不好的,估计要郁闷了。
并列第一的,还包括Navicat for MySQL,有win和mac版本。我没有用过,但是我们的技术大神潘神,一直在坚持用,而且隆重推荐。
入选理由:潘神看上的,没有差评的。
SVN管理工具
一个人做开发,可以天马行空,随意做。但是一个团队,如果没有SVN管理手段,那简直是无法想象的。那感觉,就像是在深圳福田中心区裸奔一样,原始。
SVN一般来说,在服务器端, linux系列系统的话,一般来说都有安装Subversion系列的版本管理系统,简单易用,信手拈来。
当然,系统也可以托管在Github,现在特别的火哦。对于我们的团队开发来说,商业行为嘛,还是托管在自己的Subversion服务器比较好,在局域网也好,随意都可以搭建一个的。
Mac版本,首推CornerStone,版本2.7+了,我也没有用很新的版本,这个就足够了。里面具有完整的SVN客户端的功能。不过Mac似乎没有Windows用户那么幸运,因为mac版本的版本管理工具,整合程度更高。
啦啦啦啦啦,最赞的工具出来了。Win版本的,TortoiseSVN,又称小龟壳,神器啊!
不过我最近刚刚从cornerstone换到phpstorm了。他家的svn也整合得很好。
服务器端操控工具
一般来说,我们平时是少不了要操控远程服务器的,比如阿里云什么之类的,为了性能起见一般也不会选用win的服务器,而是用linux系列的服务器。因此,服务器的客户端操控工具就尤其的重要了。
分三大类来看:
第一类是终端工具,我除了选用mac自带的之外,一般来说还会选择ZOC 和 SecureCRT。Mac下面,zoc似乎比后者要更加顺手一点,不过SecureCRT算是综合和全能的选手了吧,拥趸超级多啊!
第二类是FTP工具,在windows下面,基本上就不用介绍了,选择实在是太多了,随意选择一个就好了。Mac版本,我发现Transmit 4特别好用,就这个了。
第三类是文件比较工具,潘神隆重推荐,必备神器:Beyond Compare,可以实现服务器端和本地的文件比较,改哪行随心所欲,哈哈。不过,要注意尺度哦,很多人选了这个,就不用SVN工具了,这是大忌啊。
我们就试过,一个晚上,三个人,轮流用BeyongdCompare在更新同一个文件,结果……可想而知了,差点打起来了。
浏览器等测试工具
既然php程序狗,那就肯定是用到浏览器来测试的了。
先摆明我们的立场,有节操的程序猿,从来不用IE浏览器。用IE的,请绕道,走好,不送。
必须推荐两大神器:Chrome 和Firefox,点击菜单,选择:开发者工具,然后,你想干什么,都可以了。
如果两大神器,再配上另外一个牛逼的插件,那就更厉害了。这个插件,名字叫:Postman REST Client,可以专门用来测试post/get等各类网络访问方式,然后还能收到返回的结果,接口测试后者其他的ajax测试,都非常的方便呢。
最后,如果你是负责微信开发的,一定少不了一个二维码生成工具,这类工具太多了,mac下面iQR,很好用。也有网站生成的,随便找一个网站就好了。
产品狗修炼必备
俗话说:不想当将军的士兵,不是好士兵。
我说:不想当产品经理的程序员,不是好程序员。
哈哈,因为涉及到物种的进化和演变,要修炼为一个牛逼的产品狗,那可不是那么简单的,必须得上知天文下晓地理中间还得猜到女人的心思(据说,女人的使用习惯决定了70%以上的UI习惯)。在这里,隆重给大家介绍几个工具:
最重要的,当然是Axure RP pro,业界又称为:人品软件。在mac和win系统都有。会画:人品图,是作为一个产品狗的最基本的功夫。这个工具是用来做产品原型的,可以实现大概的 布局,并且能实现用户的交互动作的设置,是产品狗用来沟通上游用户,下游设计和程序开发的最重要的工具了。
这无异于武林之中的圣火令嘛。
另外一个,就是Mindmanager,程序员用来做需求分析和开发的安排等,也是很有用的。这个也是策划们最喜欢用的工具之一了。
要打动用户,除了能编程序,那是远远不够的,用PPT?现在估计也OUT了吧,最新的演示的工具,叫:Prezi,让你关注观点,打动客户!
链接: 提取码:x2p5
免费分享,但是X度限制严重,如若链接失效点击链接或搜索加群 群号。
public function login(Request $request)
{
$info = Validator::make($request->all(), [
‘username‘ => ‘required‘,
‘password‘ => ‘required‘,
],[
‘username.required‘ => ‘用户名必须填写‘,
‘password.required‘ => ‘密码必须填写‘,
]);
if ($info->fails()) {
//返回json数据
return response()->json([‘code‘=>"201","msg"=>$info->errors()->first()]);
}
// dd($info->errors()->first());
// return $info;
}
res = JSON.parse(result.replace(/
/g,"\n").replace(/ /g,"\r"));//转义
str_o = res.result;
str_n = str_o.replace(/\n/g,"
");//解析后再转回来
摘自:
参考:https://www.kancloud.cn/manual/thinkphp5/118006
0.在此之前安装好phpstudy
https://www.xp.cn
1.下载composer,并安装:
1.下载地址:https://getcomposer.org/Composer-Setup.exe
2.把phpstudy版本切换为5.4以上,否则安装不成功,并打开【其他选项菜单】——【打开文件位置】——【php】复制目录:D:phpStudyPHPTutorialphp
3.安装Composer-Setup.exe,一直下一步直到有项要选择php.exe文件的,打开上一步目录,选择一个对应版本的目录:D:phpStudyPHPTutorialphpphp-7.1.13-nts,继续下一步,直到安装完成
3.创建一个实例:
在D:phpStudyPHPTutorialWWW hinkphp 此处创建对应目录,进入目录,shift+右键----在此处打开命令窗口,在命令里输入如下命令创建立第一个实例:
composer create-project topthink/think=5.0.* tp5 --prefer-dist
完成之后目录文件如下:
然后、配置一下站点即可访问参考:https://www.cnblogs.com/chenxi188/p/11719125.html
总结:其实安装composer最终目的就是为了建立tp1 这个实例,想省事话,不用安装,直接复制别人建好的tp1即可。
在记录优化内容前选搞清楚web渲染流程的四个主要步骤:
解析HTML生成DOM树 - 渲染引擎首先解析HTML文档,生成DOM树
构建Render树 - 接下来不管是内联式,外联式还是嵌入式引入的CSS样式会被解析生成CSSOM树,根据DOM树与CSSOM树生成另外一棵用于渲染的树-渲染树(Render tree),
布局Render树 - 然后对渲染树的每个节点进行布局处理,确定其在屏幕上的显示位置
绘制Render树 - 最后遍历渲染树并用UI后端层将每一个节点绘制出来
以上步骤是一个渐进的过程,为了提高用户体验,渲染引擎试图尽可能快的把结果显示给最终用户。它不会等到所有HTML都被解析完才创建并布局渲染树。它会在从网络层获取文档内容的同时把已经接收到的局部内容先展示出来。
优化思路:
为了保障首屏内容的最快速显示,就需要做资源的拆分,那么以什么粒度拆分、要不要拆分,不同页面、不同场景策略不同。以达到渐进式页面渲染
优化内容:
1.html文件内容整理:
1.html文件中剥离css代码和非必要js代码,尽量不要融合到一起,毕竟多种代码混合会给你管理页面代码带来极大的负担。而且html文件内容复多少会影响html页面加载速度。
2.html文件中加载css文件
只下载和首屏页面布局有关文件,其他css文件等游戏启动后在空闲时间偷偷下载或者使用前夕再下载。因为渲染时DOM树的生成过程中可能会被CSS的加载执行阻塞
3.html文件中JavaScript 应尽量少影响 DOM 的构建,如果是引用外部插件或者平台sdk则遵循以下原则:
只下载和初始化所需有关文件(如vconsole插件),其他js文件等游戏启动后偷偷下载或者使用前夕再下载。因为渲染时DOM树的生成过程中同样可能会被JS的加载执行阻塞
如果服务器支持合并请求,就把js和css合并请求。来提高性能。因为同一域名下的请求有一定数量限制,浏览器下载静态文件超过限制数目的请求会被阻塞
2.压缩js
webpack合并所有js文件
3.压缩css
在线压缩工具压缩css
4.压缩图片
打包脚本集成pngquant和webp,压缩纹理,安卓使用webp、ios不变(注:小图不建议压缩。图片大小差异不大,还额外增加了解码耗时。数量不多忽略)
5.sdk或插件(含对应css文件)集成
从主包分离外部sdk文件、插件文件。添加js插件管理文件,html初始化下载js文件(包含:游戏主包js 文件、插件管理器js文件)。 初始化依赖插件或者SDK,插件管理器启动时直接下载,非初始化依赖文件在主包下载完成后,适时按渠道所需下载。加速主包下载和首屏创建、
插件携带css文件同理。
6.逻辑整理
独立、规范游戏流程逻辑。把能异步的同步流程改为异步执行,减少游戏流程耗时(如连接登录或者更新服务器的时间,异步下载和预览登录资源)
7.Ui渲染相关
部分需要同时渲染大量ui节点的界面改为分帧加载的方式,加速页面呈现速度
文章都是学习过程中的总结,如果发现错误,欢迎留言指出~
文章地址:
碰撞检测这个东西写小游戏挺有用der~~~
注释写的还挺全,所以就不多说了,看注释
这是页面结构。wrap存放生成的小球
box是用来检测的元素
<div class="gxlsystem.com">
<!-- 用于做碰撞检测的一个元素 -->
<div class="box"></div>
</div>
css:
body {
margin: 0;
padding: 0;
}
.wrap {
width: 100vw;
height: 100vh;
background-color: rgb(228, 243, 248);
}
.box {
width: 70px;
height: 70px;
background-color: green;
border-radius: 50%;
position: fixed;
top: 0;
left: 0;
}
.wrap .item {
position: fixed;
border-radius: 50%;
}
js
let wrap = document.querySelector(‘.wrap‘);
let box = document.querySelector(‘.box‘);
// 随机数函数
function rn(a, b) {
return Math.round(Math.random() * (a - b) + b);
}
// 创建其他小球的函数
function create() {
// 创建div
let el = document.createElement(‘div‘);
// 从20-100之间随机一个数作为小球的宽高(要是圆形,所以宽高相同)
let w = rn(20, 80);
el.style.width = w + ‘px‘;
el.style.height = w + ‘px‘;
// 设置top left值 该元素css有固定定位
//可以选择把最大值设置为当前屏幕/父级宽高,然后减去元素最大随即宽度(80)
el.style.top = rn(0, 500) + ‘px‘;
el.style.left = rn(0, 1200) + ‘px‘;
// 追加item作为类名
el.className = ‘item‘;
// 获取0-1之间的随机数并取小数点后一位
let opa = Math.random().toFixed(1);
// 设置随机颜色
// 不能先给一个变量随机0-254,然后拼接变量,拼接出来red green blue颜色都一样
el.color = ‘rgba(‘ + rn(0, 254) + ‘,‘ + rn(0, 254) + ‘,‘ + rn(0, 254) + ‘,‘ + opa + ‘)‘;
el.style.background = el.color;
// 元素追加给wrap
wrap.appendChild(el);
}
// 利用循环创建20个小球
function balls() {
for (let i = 0; i < 20; i++) {
create();
}
}
balls();
// box半径(理论上写一个就好,是个正方形画出来的圆嘛~)
let r1 = box.offsetWidth / 2;
// 获取创建的小球们
let item = document.querySelectorAll(‘.item‘);
// console.log(item);
// 文档注册鼠标移动事件
document.onmousemove = function (ev) {
let e = ev || event;
// 获取浏览器宽度/高度,减去要检测的盒子宽高一半
let x = e.clientX - r1;
let y = e.clientY - r1;
// 设为要拿去做检测的盒子左边,上边
// 鼠标移动的时候改变要拿去做碰撞检测的元素的top left值(改变位置,让他动起来,动的范围是整个可视页面-自己宽高,不会卡一半在外面)
box.style.left = x + ‘px‘;
box.style.top = y + ‘px‘;
for (let i = 0; i < item.length; i++) {
// item的半径
let r2 = item[i].offsetHeight / 2;
// 生成的小球的左边+半径,减用来检测的元素的左边+半径(两个球圆心距)
let leftC = item[i].offsetLeft + r2 - (box.offsetLeft + r1);
// 生成的小球上边加半径-检测小球的上边加半径
let topC = item[i].offsetTop + r2 - (box.offsetTop + r1);
// pow() 方法可返回 x 的 y 次幂的值。
// sqrt() 方法可返回一个数的平方根。
// 水平方向圆心距的2次幂 + 垂直方向的圆心距2次幂,的平方根(勾股定理?)
let dis = Math.sqrt(Math.pow(leftC, 2) + Math.pow(topC, 2));
// 求出来的值小于检测的两个球的半径的和,碰撞上了,生成的小球变色
if (dis < r1 + r2) {
item[i].style.background = ‘red‘;
} else {
item[i].style.background = item[i].color;
}
}
};
over
1.什么是Fluent API?
EF中内嵌的约定将POCO类映射到表。但是,有时您无法或不想遵守这些约定,需要将实体映射到约定指示外的其他对象,所以Fluent API和注解都是一种方法,这两种方法是用来配置EF在映射属性时绕开约定。Code first fluent API最常访问通过重写OnModelCreating方法在派生DbContext。
2.包含属性和排除属性
按照约定,数据模型中都包含一个getter和一个setter公共属性。
2.1包含属性
包含属性官网解释有点难以理解,我个人认为在OnModelCreating方法配置包含Blog模型,那么当我们调用Blog模型读写数据时候就会从连接数据库中读写对应Blog表。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>();
}
2.2排除属性
如果你不想往BlogMetadata上读写数据,可以使用数据批注或者fluent API从模型中排除该实体类型。
2.2.1数据批注
namespace EFModeling.DataAnnotations.IgnoreType
{
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public BlogMetadata Metadata { get; set; }
}
//读写不映射该实体
[NotMapped]
public class BlogMetadata
{
public DateTime LoadedFromDatabase { get; set; }
}
}
2.2.2Fluent API
namespace EFModeling.FluentAPI.IgnoreType
{
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//Ignore方法就是读写不映射该实体
modelBuilder.Ignore<BlogMetadata>();
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public BlogMetadata Metadata { get; set; }
}
public class BlogMetadata
{
public DateTime LoadedFromDatabase { get; set; }
}
}
3.主键
使用关系型数据库时候,都会涉及到主键概念,用作每个实体实例的主要唯一标识符。
3.1数据批注
namespace EFModeling.DataAnnotations.KeySingle
{
class MyContext : DbContext
{
public DbSet<Car> Cars { get; set; }
}
class Car
{
//设置LicensePlate为主键
[Key]
public string LicensePlate { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
}
3.2Fluent API
namespace EFModeling.FluentAPI.KeySingle
{
class MyContext : DbContext
{
public DbSet<Car> Cars { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
//设置LicensePlate为主键
.HasKey(c => c.LicensePlate);
}
}
class Car
{
public string LicensePlate { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
}
4.生成值
有三个可用于属性的值生成模式:
●无值生成:没有值生成意味着,需始终提供要保存到数据库的有效值。必须先将有效的值赋予新的实体,再将这些新的实体添加到上下文中。
●在添加时生成值:在添加时生成值,意思是为新实体生成值。
●在添加或更新时生成值:在添加或更新时生成值,意味着在每次保存该记录(插入或更新)时生成新值。
注:如果想在数据库端添加或更新时自动生成值,我们可以通过触发器和配置默认值等方法生成。例如,如果指定在添加或更新时要生成DateTime属性,则必须设置生成值的方法。若要执行此操作,一种方法是配置GETDATE() 的默认值以生成新行的值,然后即可使用数据库触发器在更新过程中生成值,如下面的示例触发器所示:
USE [Blogging]
GO
/****** Object: Trigger [dbo].[Blog_Update_Trigger] Script Date: 2019/10/22 16:18:13 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[Blog_Update_Trigger] ON [dbo].[Blog]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
DECLARE @Id INT
SELECT @Id = INSERTED.BlogId
FROM INSERTED
UPDATE dbo.Blog
SET Updatetime = GETDATE()
WHERE BlogId = @Id
END
4.1数据批注
4.1.1无值生成
public class Blog
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int BlogId { get; set; }
public string Url { get; set; }
}
4.1.2在添加时生成值
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime Inserted { get; set; }
}
4.1.3在添加或更新时生成值
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime LastUpdated { get; set; }
}
4.2Fluent API
4.2.1无值生成
modelBuilder.Entity<Blog>()
.Property(b => b.BlogId)
.ValueGeneratedNever();
4.2.2在添加时生成值
modelBuilder.Entity<Blog>()
.Property(b => b.Inserted)
.ValueGeneratedOnAdd();
4.2.3在添加或更新时生成值
modelBuilder.Entity<Blog>()
.Property(b => b.LastUpdated)
.ValueGeneratedOnAddOrUpdate();
参考文献:
1登录账号:要求由6到12位字母、数字、下划线组成,只有字母可以开头;
2登录密码:要求显示“• ”或“*”表示输入位数,密码要求八位以上字母、数字组成。
3性别:要求用单选框或下拉框实现,选项只有“男”或“女”;
4学号:要求八位数字组成,前四位为“2018”开头,输入自己学号;
5姓名:输入自己的姓名;
5电子邮箱:;
6点击“添加”按钮,将学生个人信息存储到数据库中。
until层
DBUtil.java
package util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DBUtil {
static String user="root";
static String password="";
static String url="jdbc:mysql://localhost:3306/login?&useSSL=false&serverTimezone=UTC";
//连接数据库
public static Connection getConn(){
Connection conn=null;
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
conn=DriverManager.getConnection(url, user, password);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
//关闭数据库
public void close(ResultSet rs, Statement state, Connection conn) {
if(rs!=null)
{
try
{
rs.close();
}
catch(SQLException e)
{
e.printStackTrace();
}
}
if(state!=null)
{
try
{
state.close();
}
catch(SQLException e)
{
e.printStackTrace();
}
}
if(conn!=null)
{
try
{
conn.close();
}
catch(SQLException e)
{
e.printStackTrace();
}
}
}
}
Dao层
DaoDao.java
package Dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
import util.DBUtil;
public class Dao {
static Connection conn;
static PreparedStatement ps = null;
static ResultSet rs;
static String sql = "select * from WeiHu";
static DBUtil ut= new DBUtil();
static Scanner in = new Scanner(System.in);
static int num1=1;
//平台维护信息录入
public static int add(String w1,String w2,String w3,String w4,String w5,String w6,String w7) throws SQLException
{
int b=0;
conn= ut.getConn();
String sql="insert into login1 values(?,?,?,?,?,?,?)";
try {
ps=conn.prepareStatement(sql);
ps.setString(1,w1);
ps.setString(2,w2);
ps.setString(3,w3);
ps.setString(4,w4);
ps.setString(5,w5);
ps.setString(6,w6);
ps.setString(7,w7);
int a=ps.executeUpdate();
if(a>0) {
b++;
System.out.println("添加成功");
}
else {
System.out.println("添加失败");
}
}catch(Exception e) {
e.printStackTrace();
}
try {
if(ps!=null)ps.close();
if(conn!=null)conn.close();
}catch(Exception e2) {
e2.printStackTrace();
}
return b;
}
//插入数据
public static int add_one(String w1,String w2,String w3,String w4,String w5,String w6,String w7,String w8,String w9,String w10,String w11,String w12) throws SQLException
{
int b=0;
conn= ut.getConn();
String sql="insert into register values(?,?,?,?,?,?,?,?,?,?,?,?)";
try {
ps=conn.prepareStatement(sql);
ps.setString(1,w1);
ps.setString(2,w2);
ps.setString(3,w3);
ps.setString(4,w4);
ps.setString(5,w5);
ps.setString(6,w6);
ps.setString(7,w7);
ps.setString(8,w8);
ps.setString(9,w9);
ps.setString(10,w10);
ps.setString(11,w11);
ps.setString(12,w12);
int a=ps.executeUpdate();
if(a>0) {
b++;
System.out.println("添加成功");
}
else {
System.out.println("添加失败");
}
}catch(Exception e) {
e.printStackTrace();
}
try {
if(ps!=null)ps.close();
if(conn!=null)conn.close();
}catch(Exception e2) {
e2.printStackTrace();
}
return b;
}
}
前端使用jsp
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>注册</title>
<script type="text/javascript">
function validate()
{
var Id=document.forms["myForm"]["l5"].value;
if(Id<20180000&&Id>=20190000)
{
alert("学号必须以2018开头");
return false;
}
}
</script>
</head>
<body>
<header><center><h3>添加学生信息</h3></center></header>
<hr>
<form action="Login_deal.jsp" name="myForm" onsubmit="return validate()">
<table border="1" align="center" bgcolor="#FFFFCC">
<tr>
<td>登录账号</td>
<td><input type="text" name="l1" ></td>
</tr>
<tr>
<td>登录密码</td>
<td><input type="password" name="l2"></td>
</tr>
<tr>
<td>姓别</td>
<td>
<input type="radio" name="l3">男
<input type="radio" name="l3">女
</td>
</tr>
<tr>
<td>姓名</td>
<td>
<input type="text" name="l4">
</td>
</tr>
<tr>
<td>学号</td>
<td>
<input type="text" name="l5">
</td>
</tr>
<tr>
<td>电子邮件</td>
<td><input type="text" name="l6"></td>
</tr>
<tr>
<td>所在学院</td>
<td><input type="text" name="l7"></td>
</tr>
<tr>
<td>所在系</td>
<td><input type="text" name="l8"></td>
</tr>
<tr>
<td>所在班级</td>
<td><input type="text" name="l9"></td>
</tr>
<tr>
<td>入学年份</td>
<td>
<select name="l10">
<option value="2018">2018</option>
<option value="2017">2017</option>
</select>
</td>
</tr>
<tr>
<td>生源地</td>
<td><input type="text" name="l11" value=""></td>
</tr>
<tr>
<td>备注</td>
<td><textarea rows="10" cols="30" name="l12"></textarea></td>
</tr>
<tr>
<td><input type="submit" value="添加"></td>
</tr>
</table>
</form>
</body>
</html>
login_deal.jsp
<%@page import="Dao.Dao"%>
<%@page import="java.util.regex.Pattern"%>
<%@page import="java.util.regex.Matcher"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
request.setCharacterEncoding("utf-8");
String l1=request.getParameter("l1");
String l2=request.getParameter("l2");
String l3=request.getParameter("l3");
String l4=request.getParameter("l4");
String l5=request.getParameter("l5");
String l6=request.getParameter("l6");
String l7=request.getParameter("l7");
String l8=request.getParameter("l8");
String l9=request.getParameter("l9");
String l10=request.getParameter("l10");
String l11=request.getParameter("l11");
String l12=request.getParameter("l12");
String user="^[a-zA-Z][a-zA-Z0-9_]{5,12}$";
Pattern puser = Pattern.compile(user);
Matcher muser = puser.matcher(l1);
boolean isMatch3 = muser.matches();
String pass="^[a-zA-Z][a-zA-Z0-9_]{7,20}$";
Pattern ppass = Pattern.compile(pass);
Matcher mpass = ppass.matcher(l2);
boolean isMatch2 = mpass.matches();
String mail = "^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$";
Pattern pmail = Pattern.compile(mail);
Matcher mmail = pmail.matcher(l6);
boolean isMatch1 = mmail.matches();
if(isMatch3){
if(isMatch2){
if(isMatch1){
if(Dao.add_one(l1, l2, l3, l4, l5, l6, l7,l8,l9,l10,l11,l12)==1)
{
{
out.print("<script language = ‘javascript‘>alert(‘添加成功‘);</script>");
response.setHeader("refresh", "0;url=Login.jsp");
}
}
else{
out.print("<script language = ‘javascript‘>alert(‘添加失败‘);</script>");
response.setHeader("refresh", "0;url=Login.jsp");
}
}else{
out.print("<script language = ‘javascript‘>alert(‘您的邮箱" +l6 + "是错误格式!!!‘);</script>");
out.print("<script>window.history.go(-1); </script>");
}
} else {
out.print("<script language = ‘javascript‘>alert(‘您的密码" +l2 + "是错误格式!!!应为八位以上字母数字组成!‘);</script>");
out.print("<script>window.history.go(-1); </script>");
}
}else{
out.print("<script language = ‘javascript‘>alert(‘您的用户名" +l1 + "是错误格式!!!应为6-12位以上字母数字下划线组成!字母开头‘);</script>");
out.print("<script>window.history.go(-1); </script>");
}
%>
</body>
</html>
using System.Text;
namespace Cipher.Algorithm
{
static class Caesar
{
static public string Encrypt(string input, int key)
{
StringBuilder sb = new StringBuilder();
for(int i = 0; i < input.Length; ++i)
{
if (‘a‘ <= input[i] && input[i] <= ‘z‘)
{
sb.Append((char)((input[i] - ‘a‘ + key + 26) % 26 + ‘a‘));
}
else if (‘A‘ <= input[i] && input[i] <= ‘Z‘)
{
sb.Append((char)((input[i] - ‘A‘ + key + 26) % 26 + ‘A‘));
}
else
{
sb.Append(input[i]);
}
}
return sb.ToString();
}
static public string Decrypt(string input, int key)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < input.Length; ++i)
{
if (‘a‘ <= input[i] && input[i] <= ‘z‘)
{
sb.Append((char)((input[i] - ‘a‘ - key + 26) % 26 + ‘a‘));
}
else if (‘A‘ <= input[i] && input[i] <= ‘Z‘)
{
sb.Append((char)((input[i] - ‘A‘ - key + 26) % 26 + ‘A‘));
}
else
{
sb.Append(input[i]);
}
}
return sb.ToString();
}
}
}
using System;
using System.Text;
namespace Cipher.Algorithm
{
public class Hill
{
// 矩阵阶数
private int _level;
// 加密矩阵
private long[][] _matrix;
// 解密矩阵
private long[][] _inverseMatrix = null;
private int _times = 0;
// 用于填充的无效字符
const char INVALID_CHAR = ‘A‘;
/// <summary>
/// 带阶数的构造函数
/// </summary>
/// <param name="level">矩阵阶数</param>
public Hill(int level)
{
_level = level;
while(_inverseMatrix == null)
{
_matrix = getRandomMatrix();
_inverseMatrix = getInverseMatrix(_matrix);
++_times;
}
;
}
public Hill(int level, long[][] matrix)
{
_level = level;
_matrix = matrix;
_inverseMatrix = getInverseMatrix(_matrix);
if (null == _inverseMatrix) _inverseMatrix = getNewMatrix();
}
#region Properties
public int Level
{
get
{
return _level;
}
}
/// <summary>当前矩阵
/// </summary>
public long[][] Matrix
{
get
{
return _matrix;
}
}
public long[][] InverseMatrix
{
get
{
return _inverseMatrix;
}
}
public int Times
{
get
{
return _times;
}
}
#endregion
/// <summary>
/// 得到一个新的整数矩阵
/// </summary>
/// <returns>矩阵</returns>
public long[][] getNewMatrix()
{
long[][] res = new long[_level][];
for (int i = 0; i < _level; ++i) res[i] = new long[_level];
for (int i = 0; i < _level; ++i)
for (int j = 0; j < _level; ++j) res[i][j] = 0;
return res;
}
/// <summary>
/// 得到一个n阶整数矩阵
/// </summary>
/// <param name="level">阶数</param>
/// <returns>矩阵</returns>
public static long[][] getNewMatrix(int level)
{
long[][] res = new long[level][];
for (int i = 0; i < level; ++i) res[i] = new long[level];
for (int i = 0; i < level; ++i)
for (int j = 0; j < level; ++j) res[i][j] = 0;
return res;
}
/// <summary>
/// 求关于MOD26的逆矩阵
/// </summary>
/// <param name="o">原矩阵</param>
/// <returns>逆矩阵</returns>
private long[][] getInverseMatrix(long[][] o)
{
long[][] res = getNewMatrix();
long[][] original = getNewMatrix();
for (int i = 0; i < _level; ++i)
{
for (int j = 0; j < _level; ++j)
{
if (i == j) res[i][j] = 1;
else res[i][j] = 0;
original[i][j] = o[i][j];
}
}
for (int k = 0; k <_level; ++k)
{
bool isGCD = false;
for (int i = k; i < _level; ++i)
{
if (GCD(original[i][k], 26) == 1)
{
isGCD = true;
if (i != k)
{
long[] temp1 = original[i], temp2 = res[i];
original[i] = original[k]; res[i] = res[k];
original[k] = temp1; res[k] = temp2;
}
break;
}
}
// 若矩阵一列中没有与26互素的元素,则认为该矩阵不可逆
if (!isGCD) return null;
long ie = getInverseElement(original[k][k], 26);
Console.WriteLine(original[k][k] + "的逆元是:" + ie);
if (-1 == ie) return null;
for (int j = 0; j < _level; ++j)
{
original[k][j] = (original[k][j] * ie) % 26;
res[k][j] = (res[k][j] * ie) % 26;
}
for (int i = k + 1; i < _level; ++i)
{
long l = original[i][k] / original[k][k];
for (int j = 0; j < _level; ++j)
{
// 对增广矩阵的运算
res[i][j] = getMOD((res[i][j] - l * res[k][j]), 26);
// 对原矩阵的运算
original[i][j] = getMOD((original[i][j] - l * original[k][j]), 26);
}
}
}
for (int k = _level - 1; k > 0; --k)
{
if (original[k][k] == 0) return null;
for (int i = k - 1; i >= 0; --i)
{
long l = original[i][k] / original[k][k];
// 对增广矩阵的运算
for (int j = 0; j < _level; ++j)
{
if (res[k][j] == 0) continue;
res[i][j] = getMOD((res[i][j] - l * res[k][j]), 26);
}
// 对原矩阵的运算
original[i][k] = getMOD((original[i][k] - l * original[k][k]), 26);
}
}
return res;
}
private long getMOD(long x, long m)
{
while (x < m)
{
x += m;
}
return x % m;
}
/// <summary>
/// 求a关于m的乘法逆元
/// </summary>
/// <param name="a"></param>
/// <param name="m"></param>
/// <returns>逆元</returns>
public static long getInverseElement(long a, long m)
{
long x = 0, y = 0;
long gcd = E_GCD(a, m, ref x, ref y);
if (1 % gcd != 0) return -1;
x *= 1 / gcd;
m = Math.Abs(m);
long res = x % m;
if (res <= 0) res += m;
return res;
}
/// <summary>
/// 拓展欧几里德算法
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns>GCD(a, b)</returns>
public static long E_GCD(long a, long b, ref long x, ref long y)
{
if (0 == b)
{
x = 1;
y = 0;
return a;
}
long res = E_GCD(b, a % b, ref x, ref y);
long temp = x;
x = y;
y = temp - a / b * y;
return res;
}
/// <summary>
/// 求最大公约数
/// </summary>
/// <param name="x">第一个参数</param>
/// <param name="y">第二个参数</param>
/// <returns>最大公约数</returns>
static public long GCD(long x, long y)
{
if (y == 0) return x;
return GCD(y, x % y);
}
static int GetRandomSeed()
{
byte[] bytes = new byte[4];
System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
rng.GetBytes(bytes);
return BitConverter.ToInt32(bytes, 0);
}
private long[][] getRandomMatrix()
{
long[][] res = getNewMatrix();
for (int i = 0; i < _level; ++i)
{
for (int j = 0; j < _level; ++j)
{
int t;
Random rd = new Random(GetRandomSeed());
t = rd.Next(0, 26);
res[i][j] = t;
}
}
return res;
}
private string getOneGroup(string input, long[][] matrix)
{
StringBuilder sb = new StringBuilder();
int[] p = new int[_level];
for (int i = 0; i < _level; ++i)
{
if (i < input.Length)
p[i] = input[i] - ‘A‘;
else p[i] = INVALID_CHAR;
}
for (int i = 0; i < _level; ++i)
{
long o = 0;
for (int j = 0; j < _level; ++j)
{
o += matrix[i][j] * p[j] ;
}
Console.Write(o.ToString() + " ");
sb.Append((char)(o % 26 + ‘A‘));
}
Console.WriteLine();
return sb.ToString();
}
/// <summary>
/// 加密
/// </summary>
/// <param name="input">请确保输入的字符串只有字母</param>
/// <returns></returns>
public string Encrypt(string input)
{
StringBuilder sb = new StringBuilder();
input = input.ToUpper();
for (int i = 0; i < input.Length; i += _level)
{
int end = _level < (input.Length - i) ? _level : (input.Length - i);
sb.Append(getOneGroup(input.Substring(i, end), _matrix));
}
return sb.ToString();
}
public string Decrypt(string input)
{
StringBuilder sb = new StringBuilder();
input = input.ToUpper();
for (int i = 0; i < input.Length; i += _level)
{
int end = _level < (input.Length - i) ? _level : (input.Length - i);
sb.Append(getOneGroup(input.Substring(i, end), _inverseMatrix));
}
return sb.ToString();
}
}
}
using System.Text;
using System.Windows;
namespace Cipher.Algorithm
{
public static class Playfair
{
private static char[,] _key = new char[5, 5]; // 经过处理的5×5矩阵
private static Point[] _location = new Point[26]; // 26个字母在key中的位置
private static string _group; // 分组后的字符串
private static char _ch = ‘Q‘; // 无效字母,如Q, K, X
public static string Encrypt(string input)
{
StringBuilder sb = new StringBuilder();
string str = group(input);
for(int i = 0; i < str.Length; i += 2)
{
int r1 = (int)(_location[str[i] - ‘A‘].X);
int r2 = (int)(_location[str[i + 1] - ‘A‘].X);
int c1 = (int)(_location[str[i] - ‘A‘].Y);
int c2 = (int)(_location[str[i + 1] - ‘A‘].Y);
// 字母同行
if (r1 == r2)
{
sb.Append(_key[r1, (c1 + 1) % 5]).Append(_key[r1, (c2 + 1) % 5]);
}
// 字母同列
else if (c1 == c2)
{
sb.Append(_key[(r1 + 1) % 5, c1]).Append(_key[(r2 + 1) % 5, c1]);
}
else
{
if (r1 > r2 && c1 > c2)
{
sb.Append(_key[r1, c2]).Append(_key[r2, c1]);
}
else if (r1 < r2 && c1 > c2)
{
sb.Append(_key[r2, c1]).Append(_key[r1, c2]);
}
else if (r1 > r2 && c1 < c2)
{
sb.Append(_key[r1, c2]).Append(_key[r2, c1]);
}
else
{
sb.Append(_key[r2, c1]).Append(_key[r1, c2]);
}
}
}
return sb.ToString();
}
public static string Decrypt(string input)
{
StringBuilder sb = new StringBuilder();
string str = (string)input.ToUpper();
if (str.Length % 2 == 1 || str.Length == 0 || str.IndexOf(‘ ‘) != -1) return "";
for (int i = 0; i < str.Length; i += 2)
{
int r1 = (int)(_location[str[i] - ‘A‘].X);
int r2 = (int)(_location[str[i + 1] - ‘A‘].X);
int c1 = (int)(_location[str[i] - ‘A‘].Y);
int c2 = (int)(_location[str[i + 1] - ‘A‘].Y);
// 字母同行
if (r1 == r2)
{
sb.Append(_key[r1, (c1 - 1 + 5) % 5]).Append(_key[r1, (c2 - 1 + 5) % 5]);
}
// 字母同列
else if (c1 == c2)
{
sb.Append(_key[(r1 - 1 + 5) % 5, c1]).Append(_key[(r2 - 1 + 5) % 5, c1]);
}
else
{
if (r1 > r2 && c1 > c2)
{
sb.Append(_key[r1, c2]).Append(_key[r2, c1]);
}
else if (r1 < r2 && c1 > c2)
{
sb.Append(_key[r2, c1]).Append(_key[r1, c2]);
}
else if (r1 > r2 && c1 < c2)
{
sb.Append(_key[r1, c2]).Append(_key[r2, c1]);
}
else
{
sb.Append(_key[r2, c1]).Append(_key[r1, c2]);
}
}
}
for(int i = 2; i < sb.Length; ++i)
{
if(sb[i].Equals(sb[i - 2]) && sb[i - 1].Equals(_ch))
{
sb.Remove(i - 1, 1);
}
}
if (sb[sb.Length - 1].Equals(_ch)) sb.Remove(sb.Length - 1, 1);
return sb.ToString();
}
public static char[, ] Key(string word)
{
string temp = word.ToUpper();
StringBuilder sb = new StringBuilder();
bool[] flag = new bool[26];
for(int i = 0; i < temp.Length; ++i)
{
// 该字母未出现过
if (flag[temp[i] - ‘A‘] == false)
{
sb.Append(temp[i]);
}
flag[temp[i] - ‘A‘] = true;
}
for(int i = 0; i < 26; ++i)
{
if (i == ‘J‘ - ‘A‘)
{
continue;
}
if (flag[i] == false)
{
sb.Append((char)(i + ‘A‘));
}
}
for (int i = 0; i < 5; ++i)
{
for(int j = 0; j < 5; ++j)
{
_key[i, j] = sb[i * 5 + j];
Point insert = new Point(i, j);
_location[_key[i, j] - ‘A‘] = insert;
}
}
return _key;
}
private static string group(string input)
{
StringBuilder sb = new StringBuilder();
string temp = input.ToUpper();
for(int i = 0; i < temp.Length; )
{
if (0 != i && sb.Length > 0 && temp[i] == sb[sb.Length - 1])
{
sb.Append(_ch);
}
else if (‘A‘ <= temp[i] && temp[i] <= ‘Z‘)
{
sb.Append(temp[i]);
++i;
}
else
{
++i;
}
}
if (sb.Length % 2 == 1)
{
sb.Append(_ch);
}
_group = sb.ToString();
return sb.ToString();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Cipher.Algorithm
{
class Rd
{
public static int GetRandomSeed()
{
byte[] bytes = new byte[4];
System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
rng.GetBytes(bytes);
return BitConverter.ToInt32(bytes, 0);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
namespace Cipher.Algorithm
{
class RSA
{
// 已保存的素数集
protected int[] primes = { 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389 };
protected BigInteger rsa_e;
protected BigInteger rsa_d;
protected BigInteger rsa_n;
protected BigInteger rsa_p;
protected BigInteger rsa_q;
#region Properties
public string P
{
get
{
return rsa_p.ToString();
}
}
public string Q
{
get
{
return rsa_q.ToString();
}
}
public string E
{
get
{
return rsa_e.ToString();
}
}
public string D
{
get
{
return rsa_d.ToString();
}
}
public string N
{
get
{
return rsa_n.ToString();
}
}
#endregion
public RSA()
{
BigInteger p, q;
p = getRandomPrime();
q = getRandomPrime();
while (p == q)
{
// 确保p与q不相等
q = getRandomPrime();
}
BigInteger n = p * q;
BigInteger fi_n = (p - 1) * (q - 1);
BigInteger e = getRandomPrime();
while (GCD(fi_n, e) != 1)
{
e = getRandomPrime();
}
BigInteger d = getInverseElement(e, fi_n);
rsa_e = e;
rsa_d = d;
rsa_n = n;
rsa_p = p;
rsa_q = q;
}
public RSA(BigInteger p, BigInteger q, BigInteger e)
{
rsa_p = p;
rsa_q = q;
rsa_e = e;
BigInteger n = p * q;
BigInteger fi_n = (p - 1) * (q - 1);
if (GCD(fi_n, e) != 1) return;
BigInteger d = getInverseElement(e, fi_n);
rsa_d = d;
rsa_n = n;
}
public BigInteger[] Encrypt(string input)
{
List<BigInteger> res = new List<BigInteger>();
char[] c = input.ToArray();
for (int i = 0; i < c.Length; ++i)
{
res.Add(EncryptSingle(c[i], rsa_e));
}
return res.ToArray();
}
public char[] Decrypt(BigInteger[] input)
{
List<char> res = new List<char>();
for (int i = 0; i < input.Length; ++i)
{
int ch = Int32.Parse(EncryptSingle(input[i], rsa_d).ToString());
res.Add((char)ch);
}
return res.ToArray();
}
/// <summary>
/// 对单个字符进行幂运算加密
/// </summary>
/// <param name="input"></param>
/// <param name="m"></param>
/// <returns></returns>
protected BigInteger EncryptSingle(BigInteger input, BigInteger m)
{
BigInteger res = 1;
for (int i = 0; i < m; ++i)
{
res = (res * input) % rsa_n;
}
return res;
}
protected BigInteger getRandomPrime()
{
Random rd = new Random(Rd.GetRandomSeed());
BigInteger res = new BigInteger(primes[rd.Next(0, primes.Length)]);
return res;
}
protected BigInteger GCD(BigInteger a, BigInteger b)
{
if (b == BigInteger.Zero) return a;
return GCD(b, a % b);
}
/// <summary>
/// 求a关于m的乘法逆元
/// </summary>
/// <param name="a">原数</param>
/// <param name="m">被MOD的数</param>
/// <returns>逆元</returns>
protected BigInteger getInverseElement(BigInteger a, BigInteger m)
{
BigInteger x = 0, y = 0;
BigInteger gcd = E_GCD(a, m, ref x, ref y);
if (1 % gcd != 0) return -1;
x *= 1 / gcd;
m = BigInteger.Abs(m);
BigInteger res = x % m;
if (res <= 0) res += m;
return res;
}
/// <summary>
/// 拓展欧几里德算法
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns>GCD(a, b)</returns>
protected BigInteger E_GCD(BigInteger a, BigInteger b, ref BigInteger x, ref BigInteger y)
{
if (0 == b)
{
x = 1;
y = 0;
return a;
}
BigInteger res = E_GCD(b, a % b, ref x, ref y);
BigInteger temp = x;
x = y;
y = temp - a / b * y;
return res;
}
}
}
2011年,Taylor Otwell将Laravel作为一种包含全新现代方法的框架介绍给大家。Laravel最初的设计是为了面向MVC架构的,它可以满足如事件处理、用户身份验证等各种需求。另外它还有一个由管理数据库强力支持,用于管理模块化和可扩展性代码的软件包管理器。
Laravel以其简洁、优雅的特性赢得了大家的广泛关注,无论是专家还是新手,在开发PHP项目的时候,都会第一时间的想到Laravel。本文我们将讨论为什么Laravel会成为最成功的PHP框架。
模块化和可扩展性
Laravel注重代码的模块化和可扩展性。你可以在包含超过5500个程序包的Packalyst目录中找到你想要添加的任何文件。Laravel的目标是让你能够找到任何想要的文件。
微服务和程序接口
Lumen是一个由laravel衍生的专注于精简的微框架。它高性能的程序接口可让你更加简单快速的开发微型项目。Lumen使用最小的配置集成了所有laravel的重要特性,你可以通过将代码复制到laravel项目的方式将完整的框架迁移过来。
1
2
3
4
5
6
HTTP路径
Laravel拥有类似于Ruby on Rails的,快速、高效的路由系统。它可以让用户通过在浏览器上输入路径的方式让应用程序的各部分相关联。
1
2
3
HTTP中间件
应用程序可受到中间件的保护——中间件会处理分析和过滤服务器上的HTTP请求。你可以安装中间件,用于验证注册用户,并避免如跨站脚本(XSS)或其它的安全状况的问题。
input(‘age‘) <= 200) {
return redirect(‘home‘);
}
return $next($request);
}
缓存
你的应用程序可得到一个健壮的缓存系统,通过对其进行调整,可以让应用程序的加载更加快速,这可以给你的用户提供最好的使用体验。
Cache::extend(‘mongo‘, function($app) {
return Cache::repository(new MongoStore);
});
身份验证
安全是至关重要的。Laravel自带对本地用户的身份验证,并可以使用“remember” 选项来记住用户。它还可以让你例如一些额外参数,例如显示是否为活跃的用户。
if (Auth::attempt([‘email‘ => $email, ‘password‘ => $password, ‘active‘
=> 1 ], $remember)) {
// The user is being remembered...
}
种类集成
Laravel Cashier可以满足你要开发支付系统所需要的一切需求。除此之外,它还同步并集成了用户身份验证系统。所以,你不再需要担心如何将计费系统集成到开发当中了。
$user = User::find(1);
$user>subscription(‘monthly‘)>create($creditCardToken);
任务自动化
Elixir是一个可让我们使用Gulp定义任务的Laravel程序接口,我们可以使用Elixir定义可精简CSS
和JavaScript的预处理器。
elixir(function(mix) {
mix.browserify(‘main.js‘);
});
加密
一个安全的应用程序应该做到可把数据进行加密。使用Laravel,可以启用OpenSSL安全加密算法AES256CBC来满足你所有的需求。另外,所有的加密值都是由检测加密信息是否被改变的验证码所签署的。
use IlluminateContractsEncryptionDecryptException;
try {
$decrypted = Crypt::decrypt($encryptedValue);
} catch (DecryptException $e) {
//
}
事件处理
应用程序中事件的定义、记录和聆听都非常迅速。EventServiceProvider事件中的listen包含记录在你应用程序上所有事件的列表。
protected $listen = [
‘AppEventsPodcastWasPurchased‘ => [
‘AppListenersEmailPurchaseConfirmation‘,
],
];
分页
在Laravel中分页是非常容易的因为它能够根据用户的浏览器当前页面生成一系列链接。
paginate(15);
return view(‘user.index‘, [‘users‘ => $users]);
}
}
对象关系图(ORM)
Laravel包含一个处理数据库的层,它的对象关系图被称为Eloquent。另外这个对象关系图也适用于PostgreSQL。
$users = User::where(‘votes‘, ‘>‘, 100)>take(10)>get();
foreach ($users as $user) {
var_dump($user>name);
}
单元测试
单元测试的开发是一个耗费大量时间的任务,但是它却是保证我们的应用程序保持正常工作的关键。Laravel中可使用PHPUnit执行单元测试。
visit(‘/‘)
>see(‘Laravel 5‘)
>dontSee(‘Rails‘);
}
}
待办事项清单
Laravel提供在后台使用待办事项清单(to do list)处理复杂、漫长流程的选择。它可以让我们异步处理某些流程而不需要用户的持续导航。
㈠HSLA(H,S,L,A) 的参数说明:
H:Hue(色调)。0(或360)表示红色,120表示绿色,240表示蓝色,也可取其他数值来指定颜色。取值为:0 - 360
S:Saturation(饱和度)。取值为:0.0% - 100.0%
L:Lightness(亮度)。取值为:0.0% - 100.0%
A:Alpha透明度。取值0~1之间。
说明:
HSL记法
此色彩模式与相同,只是在模式上新增了Alpha透明度。
兼容性:
浅绿 = 支持
红色 = 不支持
粉色 = 部分支持
导包:
1 <!-- easypoi的支持 -->
2 <dependency>
3 <groupId>cn.afterturn</groupId>
4 <artifactId>easypoi-base</artifactId>
5 <version>3.2.0</version>
6 </dependency>
7 <dependency>
8 <groupId>cn.afterturn</groupId>
9 <artifactId>easypoi-web</artifactId>
10 <version>3.2.0</version>
11 </dependency>
12 <dependency>
13 <groupId>cn.afterturn</groupId>
14 <artifactId>easypoi-annotation</artifactId>
15 <version>3.2.0</version>
16 </dependency>
一:文件下载
1:前台准备导出按钮
1 <button class="easyui-linkbutton" iconCls="icon-search">数据导出</button>
1.2 准备好view(beanName的视图解析器)
在applicationContext-mvc.xml
<!--配置一下beanName的视图解析器
p:order="0":设置属性优先级
-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="0" />
<!--
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="0" />
</bean>
-->
<!-- 对于easypoi中准备发的视图(view)的扫描 -->
<context:component-scan base-package="cn.afterturn.easypoi.view" />
1.3:在DownloadController完成后台导出
/**
* 导出就是下载(下载就是把一个流 从服务器端 -> 客户端)
*
* EasypoiSingleExcelView : 注解导出的view
*/
@RequestMapping("/export")
public String export(EmployeeQuery query,ModelMap map, HttpServletRequest request){
//根据查询条件拿到所有数据
List<Employee> list = employeeService.queryAll(query);
//导出的属性
ExportParams params = new ExportParams("员工数据", "员工表", ExcelType.XSSF);
//params.setFreezeCol(2); 冻结
//request:获取到真实路径
String realPath = request.getServletContext().getRealPath("");
System.out.println(realPath);
list.forEach(e->{
e.setHeadImage(realPath+e.getHeadImage());
});
map.put(NormalExcelConstants.DATA_LIST, list); // 数据集合
map.put(NormalExcelConstants.CLASS, Employee.class);//导出实体
map.put(NormalExcelConstants.PARAMS, params);//参数
map.put(NormalExcelConstants.FILE_NAME, "employee");//文件名称
//根据返回的名称去找一个bean对象(在SpringMVC称之为view)
// /WEB-INF/views/easypoiExcelView
// 我们直接要文档的的操作是没有成功的 2.文档的情况和我们项目中的情况有一点区别
return NormalExcelConstants.EASYPOI_EXCEL_VIEW;//View名称 "easypoiExcelView"
}
二:上传功能
1:配置上传解析器注意,名字必需叫
1 <!--配置上传解析器-->
2 <bean id="multipartResolver"
3 class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
4 <!-- 默认编码 -->
5 <property name="defaultEncoding" value="utf-8" />
6 <!-- 文件大小最大值 -->
7 <property name="maxUploadSize" value="10485760000" />
8 <!-- 内存中的最小值 -->
9 <property name="maxInMemorySize" value="40960" />
10 </bean>
2 准备导入页面
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <html>
3 <head>
4 <title>Title</title>
5 <%@ include file="/WEB-INF/views/head.jsp"%>
6 </head>
7 <body>
8
9 <%--
10 上传只能是post请求
11 上传必需设置 : enctype="multipart/form-data"
12 --%>
13 <!-- 上传必需是:post,enctype="multipart/form-data"-->
14 <form action="/import/employeeXlsx" method="post" enctype="multipart/form-data">
15 <input class="easyui-filebox" name="empFile" />
17 <button class="easyui-linkbutton">导入</button>
18 </form>
19 </body>
20 </html>
3: 后台ImportController完成导入功能
1 @Controller
2 @RequestMapping("/import")
3 public class ImportController extends BaseController {
4
5 @Autowired
6 private IDepartmentService departmentService;
7
8 @RequestMapping("/index")
9 public String index(){
10 return "import";
11 }
12
13
14
15 /**
16 * 上传的文件使用 MultipartFile类型来接收
17 * @param empFile
18 * @return
19 */
20 @RequestMapping("/employeeXlsx")
21 public String employeeXlsx(MultipartFile empFile) throws Exception {
22 // System.out.println(empFile);
23 // System.out.println(empFile.getName()); //empFile:上传控件的名称
24 // System.out.println(empFile.getOriginalFilename()); //emp-poi.xlsx:上传文件的名称
25 // System.out.println(empFile.getContentType()); //mime类型 office excel 2007
26 // System.out.println(empFile.getSize()); //文件大小
27 //我们需要拿到上传的文件(输入流)
28 // empFile.getInputStream()
29 //一.使用EasyPoi获取文件数据
30 //1.1 设置参数
31 ImportParams params = new ImportParams();
32 //params.setTitleRows(1);
33 params.setHeadRows(1);
34 // 1.2 拿到文件中的数据
35 List<Employee> list = ExcelImportUtil.importExcel(empFile.getInputStream(),
36 Employee.class, params);
37 //二.保存员工数据
38 list.forEach(e->{
39 //导入时加上一个默认密码
40 e.setPassword("123");
41 //通过部门名称拿到部门数据(要有id)
42 Department dept = departmentService.findByName(e.getDepartment().getName());
43 //把部门设置到员工数据中
44 e.setDepartment(dept);
45 employeeService.save(e);
46 } );
47
48 return "import";
49 }
50
51 }
三.导入验证功能
1 导入验证包支持 pom.xml
1 <!-- JSR 303 规范验证包 -->
2 <dependency>
3 <groupId>org.hibernate</groupId>
4 <artifactId>hibernate-validator</artifactId>
5 <version>5.2.4.Final</version>
6 </dependency>
2 domain中添加验证方法
注解普通验证
1 @Entity
2 @Table(name = "employee")
3 public class Employee extends BaseDomain {
4
5 @Excel(name = "用户名")
6 @NotNull(message = "用户名不能空")
7 private String username;
8 private String password;
9 @Excel(name = "年龄")
10 @Max(value = 100)
11 @Min(value = 18)
12 private Integer age;
13 @Excel(name = "邮箱",width = 20)
14 @NotNull
15 private String email;
16 ...
17 }
3:自定义验证
1 实现IExcelVerifyHandler
2
3 4
5 /**
6 * 自定义验证(我们会在这里做唯一性的验证)
7 */
8 @Component
9 public class EmployeeExcelVerifyHandler implements IExcelVerifyHandler<Employee> {
10
11 @Autowired
12 private IEmployeeService employeeService;
13 /**
14 *
15 * ExcelVerifyHandlerResult
16 * suceess :代表验证成功还是失败(如果用户名重复,就代表失败)
17 * msg:失败的原因
18 */
19 @Override
20 public ExcelVerifyHandlerResult verifyHandler(Employee employee) {
21
22 ExcelVerifyHandlerResult result = new ExcelVerifyHandlerResult(true);
23 //如果根据用户名获取到用户,代表这个用户已经存在
24 Employee tempEmp = employeeService.findByUsername(employee.getUsername());
25 if(tempEmp!=null){
26 result.setSuccess(false);
27 result.setMsg("用户名重复");
28 }
29
30 return result;
31 }
32 }
把这个类交给Spring管理(千万不要忘了让SpringMvc去扫描到它)
1 <!--扫描工具包-->
2 <context:component-scan base-package="com.logo.aisell.util"/>
4实现验证功能
@Controller
@RequestMapping("/import")
public class ImportController extends BaseController {
@Autowired
private IEmployeeService employeeService;
@Autowired
private IDepartmentService departmentService;
@Autowired
private EmployeeExcelVerifyHandler employeeExcelVerifyHandler;
@RequestMapping("/index")
public String index(){
return "import";
}
@RequestMapping("/employeeXlsx")
public String employeeXlsx(MultipartFile empFile, HttpServletResponse response) throws Exception {
//一.使用EasyPoi获取文件数据
ImportParams params = new ImportParams();
params.setHeadRows(1);
params.setNeedVerfiy(true); //设置验证支持
params.setVerifyHandler(employeeExcelVerifyHandler); //设置一个验证处理器
//二.获取excel中的数据,封装成了一个结果对象(有很多东西)
ExcelImportResult<Employee> result = ExcelImportUtil.importExcelMore(
empFile.getInputStream(),
Employee.class, params);
//三.获到正确的数据,并把它们保存到数据库
List<Employee> list = result.getList();
list.forEach(e->{
e.setPassword("123");
Department dept = departmentService.findByName(e.getDepartment().getName());
e.setDepartment(dept);
employeeService.save(e);
});
//四.如果有错误,把错误数据返回到前台(让前台下载一个错误的excel)
//4.1判断是否有错误
if(result.isVerfiyFail()){
//4.2拿到错误的文件薄
Workbook failWorkbook = result.getFailWorkbook();
//把这个文件导出
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); //mime类型
response.setHeader("Content-disposition", "attachment;filename=error.xlsx"); //告诉浏览下载的是一个附件,名字叫做error.xlsx
response.setHeader("Pragma", "No-cache");//设置不要缓存
OutputStream ouputStream = response.getOutputStream();
failWorkbook.write(ouputStream);
ouputStream.flush();
ouputStream.close();
}
return "import";
}
}
测试文件代码如下:
<?php
/**
* Created by PhpStorm.
* User: Yang
* Date: 2019/10/16
* Time: 10:25
*/
// 计算和
// 计算和
// 计算和
$a = 1;
$b = 2;
$c = $a+$b; //总和
/*
* 求和函数
*/
function sum($a, $b) {
return $a + $b; //返回值
}
# 第二种注释
$a = 1;
$b = 2;
## 求乘积
$c = $a * $b; # 结果
//特殊
$usedFuncs = "abcd";
preg_split("https://is", implode("", $usedFuncs), -1, preG_SPLIT_NO_EMPTY);
去除注释代码如下:
/**
* Created by PhpStorm.
* User: 25754
* Date: 2019/10/17
* Time: 9:54
*/
function removeComment($content)
{
return preg_replace("/(/*(s|.)*?*/)|(//.(s|.*)
)|(#(s*)?(.*))/", ‘‘, str_replace(array("
", " "), "
", $content));
}
$content = file_get_contents("https://www.gxlsystem.com/test.php");
echo removeComment($content);
结果代码如下:
$a = 1;
$b = 2;
$c = $a+$b;
function sum($a, $b) {
return $a + $b; }
$a = 1;
$b = 2;
$c = $a * $b;
$usedFuncs = "abcd";
preg_split("
注意:代码中有//的,都会去除
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>预览</title>
<script src="https://www.gxlsystem.com/js/jquery.min.js"></script>
<script src="https://www.gxlsystem.com/js/jquery.qrcode.min.js"></script> <style>
.topicsCode{
width: 255px;
height: 255px;
text-Align:center;
}
.topicsCode{
position: absolute;
top: 20%;
left: 39.5%;
}
#qrcode{
margin: 0;
padding: 0;
}
.prompt{
margin-top:5px;
font-size:15px;
}
</style>
</head>
<body>
<div class="topicsCode">
<div id="qrcode"></div>
<p class="prompt">请扫描二维码在手机端预览</p>
</div>
<script>
$(function(){
$(‘#qrcode‘).qrcode(‘http://www.baidu.com/‘);
})
</script>
</body>
</html>
通过百度旗下的免费平台和搜狗、360等旗下免费平台,在发布信息的时候,把你的核心词加上长尾词融入进去,同时标题也要有核心词体现,发布的内容、图片等质量要高,信息客观真实有吸引力,这些平台你都把要做的核心词、长尾词融入到标题和内容。
内容不要关于不同职业不同网站的需求,关键词的优化布局不相同,以餐饮的网站为例共享几点关键词优化排名的办法:
**一、如果有做百度、搜狗、360等竞价推行的话,只需布局妥当,根本上一个星期就能够做到首页
二、挑选1到2个核心词,且指数在100到200之间,品牌词一个;接着挑选5到15个相关度与之十分高的长尾关键词,做好记录。三、把品牌词和长尾词放在网站的主页、内容页、代码页等多个地方,做好全部内部优化后,那接下来就简单多了。
HTTP 通信时,除客户端和服务器外,还有一些用于协助通信的应用程序。如下列出比较重要的几个:代理、缓存、网关、隧道、Agent 代理。
1.代理
代理
HTTP 代理服务器是 Web 安全、应用集成以及性能优化的重要组成模块。代理位于客户端和服务器端之间,接收客户端所有的 HTTP 请求,并将这些请求转发给服务器(可能会对请求进行修改之后再进行转发)。对用户来说,这些应用程序就是一个代理,代表用户访问服务器。
出于安全考虑,通常会将代理作为转发所有 Web 流量的可信任中间节点使用。代理还可以对请求和响应进行过滤,安全上网或绿色上网。
2. 缓存
浏览器第一次请求:
浏览器第一次请求
浏览器再次请求:
浏览器再次请求
Web 缓存或代理缓存是一种特殊的 HTTP 代理服务器,可以将经过代理传输的常用文档复制保存起来。下一个请求同一文档的客户端就可以享受缓存的私有副本所提供的服务了。客户端从附近的缓存下载文档会比从远程 Web 服务器下载快得多。
3. 网关
HTTP / FTP 网关
网关是一种特殊的服务器,作为其他服务器的中间实体使用。通常用于将 HTTP 流量转换成其他的协议。网关接收请求时就好像自己是资源的源服务器一样。客户端可能并不知道自己正在跟一个网关进行通信。
4. 隧道
HTTP/SSL 隧道
隧道是会在建立起来之后,就会在两条连接之间对原始数据进行盲转发的 HTTP 应用程序。HTTP 隧道通常用来在一条或多条 HTTP 连接上转发非 HTTP 数据,转发时不会窥探数据。
HTTP 隧道的一种常见用途就是通过 HTTP 连接承载加密的安全套接字层(SSL)流量,这样 SSL 流量就可以穿过只允许 Web 流量通过的防火墙了。
5. Agent 代理
自动搜索引擎“网络蜘蛛”
Agent 代理是代表用户发起 HTTP 请求的客户端应用程序。所有发布 Web 请求的应用程序都是 HTTP Agent 代理。
By manipulating different selectors and properties, you can make interesting shapes. One of the easier ones to try is a crescent moon shape.
For this challenge you need to work with the property that sets the shadow of an element, along with the property that controls the roundness of the element‘s corners.
You will create a round, transparent object with a crisp shadow that is slightly offset to the side - the shadow is actually going to be the moon shape you see.
In order to create a round object, the property should be set to a value of 50%.
You may recall from an earlier challenge that the property takes values for , , , and a color value in that order. The and values are optional.
原始代码下的图形如下:
练习题:
Manipulate the square element in the editor to create the moon shape.
First, change the to transparent, then set the property to 50% to make the circular shape.
Finally, change the property to set the to 25px, the to 10px, to 0, to 0, and color to blue.
先把蓝色的背景调成透明
再调整后面绿色box-shadow阴影的边界半径,到50%
最后是微调box-shadow,之前做过关于这个的练习,做过我都已经忘记了,所以自己调了下不同值的看了下效果。基本就是把新月的颜色从绿色改到蓝色,边缘模糊的变锐化了,以后遇到再细看吧
练习代码:
<style>
.center {
position: absolute;
margin: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100px;
height: 100px;
background-color: transparent;
border-radius: 50%;
box-shadow: 25px 10px 0 0 blue;
}
</style>
<div class="center"></div>
效果:
小结
HTML表单提交的方法有get方法和post方法,get方法的作用是从指定的资源请求数据,post方法的作用是向指定的资源提交要被处理的数据。HTML表单一直都是Web的核心技术之一,有了它我们才能在Web上进行各种各样的应用。HTML5 Forms新增了许多新控件及其API,方便我们做更复杂的应用,而不用借助其它Javascript框架。HTML5新增表单元素有<datalist>、<keygen>和<output>。<datalist>元素规定输入域的选项列表;<keygen>元素的作用是提供一种验证用户的的可靠方法;<output>元素用于不同类型的输出,比如计算或脚本输出。HTML5拥有多个新的表单输入类型,这些新特性提供了更好的输入控制和验证,如“email”类型的文本框可以验证邮箱并提供提示。新增的表单属性用于对表单或表单文本域进行控制,比如控制表单的自动完成、自动填充功能和文本域的提示(hint)、正则匹配等功能。
习题
1.以下哪项不是HTML5新增的form元素?(D)
A.datalist B.keygen
C.output D.novalidate
2.以下不是input在html5的新类型的是(B)
A.DateTime B.file
C.Colour D.Range
3.在HTML5中,onblur和onfocus是(B)
A.HTML元素 B.样式属性
C.事件属性 D.表单属性
4.在HTML5中,哪个属性用于规定输入字段是必填的?(A)
A.required B.formvalidate
C.validate D.placeholder
5.哪种输入类型定义滑块控件?(D)
A.search B.controls
C.slider D.range
6.在下列的 HTML 中,哪个可以产生复选框?(C)
A.<input type="check"> B.<checkbox>
C.<input type="checkbox"> D.<check>
7.关于html5说法正确的是:(D)
A.HTML5是在原有HTML上的升级版
B.HTML可以不需要DTD
C.没有<!DOCTYPE html>HTML5可以正常工作
D.<output>是html5的新标签
8.哪种输入类型用于定义周和年控件(无时区)?(B)
A.date B.week
C.year D.time
9.在url类型的输入框中,输入以下哪项url不会出现错误提示?(C)
A.www.itxdl.cn B.https://itxdl.cn
C.https://www.itxdl.cn D.itxdl.cn
10.生成类型为range的<input>标签,”min”值为0,”max”值为100,”step”为3。输入哪一项的数字可以让此文本框通过验证?(C)
A.102 B.10
C.54 D.100
11.GET和POST的区别,何时使用POST?
GET:一般用于信息获取,使用URL传递参数,对所发送信息的数量也有限制,一般在2000个字符
POST:一般用于修改服务器上的资源,对所发送的信息没有限制。GET方式需要使用Request.QueryString来取得变量的值,而POST方式通过Request.Form来获取变量的值,也就是说Get是通过地址栏来传值,而Post是通过提交表单来传值。然而,在以下情况中,请使用 POST 请求:无法使用缓存文件(更新服务器上的文件或数据库) 向服务器发送大量数据(POST 没有数据量限制)发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
12.HTML5 有哪些新增的表单元素?
答案:
HTML5新增了很多表单元素让开发者构建更优秀的Web应用程序。新增表单元素有:datalist、datetime、output、keygen、date、month、week、time、color、number、range、email、url
linux上的文件sz到window编辑后多出^M,
方法一:
1、grep -anR ‘^M‘ filename |wc -l
2、crontab -e 或vim filename
3、:set ff //查看文件格式
4、:set ff=unix //设置文件格式
5、:wq!
6、grep -anR ‘^M‘ filename |wc -l
方法二
sed -i "s/$(echo -e ‘015‘)//g" filename
一、安装环境
本次部署使用阿里云ECS
操作系统: Ubuntu 18.04 64位
实例规格: 2U4G
二、kubernetes 版本
k8s.gcr.io/kube-apiserver:v1.16.2
k8s.gcr.io/kube-controller-manager:v1.16.2
k8s.gcr.io/kube-scheduler:v1.16.2
k8s.gcr.io/kube-proxy:v1.16.2
k8s.gcr.io/pause:3.1
k8s.gcr.io/etcd:3.3.15-0
k8s.gcr.io/coredns:1.6.2
三、shell
# 添加镜像源
cat <<EOF >/etc/apt/sources.list.d/docker-k8s.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable
EOF
# 更新并安装
apt update && apt install -y docker-ce kubelet kubeadm kubectl
# 关闭 swap
swapoff -a
vim /etc/fstab
# 注释掉这一行
# /swapfile none swap sw 0 0
设置阿里云镜像加速
进入阿里云;=》容器镜像服务=》镜像中心 =》 镜像加速器
页面会提供配置文档;当前只给出主要代码,如果没有阿里云账号,此步骤可以忽略;
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-‘EOF‘
{
"registry-mirrors": ["https://{阿里云分配的地址前缀}.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
安装镜像包
这里需要注意:kubernetes 镜像包是放在 google 容器托管平台,国内下载会出现超时等情况
vi k8s-image-pull.sh
# 1、将对应的包从国内镜像上拉下来
# 2、在tag成脚本中需要的image名称
# 3、移除多余的image
for i in `kubeadm config images list`; do
imageName=${i#k8s.gcr.io/}
docker pull registry.aliyuncs.com/google_containers/$imageName
docker tag registry.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName
docker rmi registry.aliyuncs.com/google_containers/$imageName
done;
初始化
# kubeadm初始化
kubeadm init --pod-network-cidr=10.244.0.0/16 -–apiserver-advertise-address=0.0.0.0 --ignore-preflight-errors=NumCPU
# --apiserver-bind-port API server 将绑定的端口。默认为 6443。
# --apiserver-advertise-address 这是 API server 用来告知集群中其它成员的地址,这也是在 init 流程的时候用来构建 命令行的地址。
如果不设置(或者设置为 0.0.0.0)那么将使用默认接口的 IP 地址。该地址也被添加到 API Server 使用的证书中。
# --ignore-preflight-errors=NumCPU 如果只有一个 cpu 请加参数
# kubeadm init 输出的 token 用于 master 和加入节点间的身份认证,token 是机密的,需要保证它的安全,因为拥有此标记的人都可以随意向集群中添加节点。
# 设置网络插件
export KUBECONFIG=/etc/kubernetes/admin.conf
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
安装 kubernetes-dashboard
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta5/aio/deploy/recommended.yaml
# 在需要做外网映射的情况下,我们使用nodeport方式访问,此段也可以忽略
vi recommended.yaml
# spec:下边添加 type: NodePort
# targetPort 下边添加 nodePort: 30001
下载kubernetes-dashboard 镜像
# 拉取 kubernetes-dashboard 镜像, 注意版本, 可在 kubernetes-dashboard.yaml 文件中查看
docker pull kubernetesui/dashboard:v2.0.0-beta5
# 官方Kubernetes仪表板映像已从注册表移至。
# 我们仍将尝试从提供图像,但是要推送这些图像,需要Google员工的帮助。
# 目前只有从 v2.0.0-beta1 以及以后的版本
# 安装 kubernetes-dashboard
kubectl create -f kubernetes-dashboard.yaml
# 查看安装结果
kubectl get pod --namespace=kubernetes-dashboard
kubectl get pod --namespace=kube-system
# 从 v2.0.0-beta1 以及以后的版本,已将仪表板从移至名称空间
# 查看端口
kubectl get svc --namespace=kube-system
或者
kubectl get svc --namespace=kubernetes-dashboard
查看账号
kubectl get sa --all-namespaces
cat <<EOF > admins.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: kubernetes-dashboard
name: admins
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admins
namespace: kubernetes-dashboard
EOF
# 创建用户
kubectl create -f admins.yaml
获取用户token
kubectl describe serviceaccount admins -n kubernetes-dashboard
获取token
kubectl describe secret admins-token-r5thw -n kubernetes-dashboard
copy token
然后访问外网地址
https://外网ip:30001
选择token 贴入 copy的token信息
作者:zhangwenjian
出处:
转移:
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
关键字: 工作流程管理系统 工作流引擎 asp.net工作流引擎 java工作流引擎. 表单引擎 工作流功能说明 工作流设计 工作流快速开发平台 业务流程管理 bpm工作流系统 java工作流主流框架 自定义工作流引擎
应用背景:
驰骋工作流系统大大小小的功能高达2000多项,所以不是多有的功能都能被企业所使用的,为了系统的简介他们需要隐藏一些暂时用不到的功能,但是一段时间后可能就需要某些原来隐藏掉的功能,所以单纯的从代码里隐藏是不够理想的,这时候就需要做一个同意管理的功能来自由处理这些流程功能和表单功能甚至是字段属性的功能。
功能配置:
需要隐藏的功能直接在下列的复选框里勾选之后点击保存就可以了,被隐藏的功能就不会在属性里显示了。 流程属性功能的隐藏显示
节点属性功能的隐藏显示
表单属性功能的隐藏显示
字段属性的隐藏显示
隐藏的模块分为,功能设置和字段设置:
功能设置是指,流程属性,表单属性,节点属性,字段属性的左侧导航栏里的分类信息
如图
字段设置是指,右侧页面的所有展示信息,如上图右侧tab页面里的功能
总结:就是流程引擎的所有功能都可以实现有选择性的使用,不用再局限于功能太多不好管理,真正的让你实现完全的流程个性化定制。
更多驰骋表单引擎、流程引擎资源:
开源-源代码下载地址:
在线文档:
视频教程:
资源下载:
Django之路由层
一 路由的作用
? 路由即请求地址与视图函数的映射关系,如果把网站比喻为一本书,那路由就好比是这本书的目录,在Django中路由默认配置在urls.py中,如下图:
二 简单的路由配置
案例:
urls.py文件
views.py文件
测试:
注意一:
刚刚我们在浏览器输入: r‘^index/$‘。
注意二:
但是我们在浏览器输入:( r‘^index/$‘匹配的是必须以 / 结尾,所以不会匹配成功index),但实际上仍然会看到结果 index page...,原因如下:
在配置文件settings.py中有一个参数APPEND_SLASH,该参数有两个值True或False
当APPEND_SLASH=True(如果配置文件中没有该配置,APPEND_SLASH的默认值为True),并且用户请求的url地址的路径部分不是以 / 结尾,例如请求的url地址是 / (即index/)再去路由表中匹配,如果匹配失败则会返回路径未找到,如果匹配成功,则会返回重定向信息给浏览器,要求浏览器重新向 。
当APPEND_SLASH=False时,则不会执行上述过程,即一旦url地址的路径部分匹配失败就立即返回路径未找到,不会做任何的附加操作
三 分组
什么是分组、为何要分组呢?比如我们开发了一个博客系统,当我们需要根据文章的id查看指定文章时,浏览器在发送请求时需要向后台传递参数(文章的id号),可以使用
针对后一种方式Django就需要直接从路径中取出参数,这就用到了正则表达式的分组功能了,分组分为两种:无名分组与有名分组
3.1 无名分组
urls.py文件
views.py文件
测试:
3.2 有名分组
urls.py文件
views.py文件
测试:
总结:有名分组和无名分组都是为了获取路径中的参数,并传递给视图函数,区别在于无名分组是以位置参数的形式传递,有名分组是以关键字参数的形式传递。
强调:无名分组和有名分组不要混合使用
四 路由分发
随着项目功能的增加,app会越来越多,路由也越来越多,每个app都会有属于自己的路由,如果再将所有的路由都放到一张路由表中,会导致结构不清晰,不便于管理,所以我们应该将app自己的路由交由自己管理,然后在总路由表中做分发,具体做法如下:
1 创建两个app
2 在每个app下手动创建urls.py来存放自己的路由,如下:
app01下的urls.py文件
app01下的views.py
app02下的urls.py文件
app02下的views.py
3 在总的urls.py文件中(mysite2文件夹下的urls.py)
测试:
五 反向解析
? 在软件开发初期,url地址的路径设计可能并不完美,后期需要进行调整,如果项目中很多地方使用了该路径,一旦该路径发生变化,就意味着所有使用该路径的地方都需要进行修改,这是一个非常繁琐的操作。
解决方案就是在编写一条url(regex, view, kwargs=None, name=None)时,可以通过参数name为url地址的路径部分起一个别名,项目中就可以通过别名来获取这个路径。以后无论路径如何变化别名与路径始终保持一致。
上述方案中通过别名获取路径的过程称为反向解析
案例:登录成功跳转到index.html页面
在urls.py文件中
在views.py中
login.html
index.html
测试:
总结:
2019.11.9 的中国.NET开发者峰会将在上海举办,到目前为止,大会的主题基本确定,这两天就会和大家会面,很多社区的同学基于对社区的信任在我们议题没有确定的情况下已经购票超过了300张,而且分享的主题都来自于社区,来自于生产实践之中的经验分享,内容之中有一点非常值得分享-基于k8s的微服务实践内容很多,但是每一个分享的时间只有30分钟,难以全面阐述k8s 这样的一个大主题,因此陈计节、陈作、刘腾飞和我又特别策划了一个11.10号的workshop活动,采用一天的时间来带领大家使用.NET Core 和Kuberntes来构建微服务,实践DevOps流程,活动得到了微软云的大力支持,为我们提供了workshop 活动的环境支持,当天参与现场活动的同学都可以体验使用微软Azure Kubernetes Service 和.NET Core 构建微服务的实践,当然我们的动手实践内容考虑到了Kubernetes的普遍性,完全基于原生kubernetes内容构建,你在自己的电脑上也可以使用Minikube 跑这套环境。
由于我们的kubernetes 资源有限,现场活动的名额有限,为了保证公平性,我们计划从现有报名参与的同学中通过抽奖形式抽取活动报名资格,通过抽奖获得报名资格,凭借现场门票报名。我们社区组织的活动属于非盈利活动,会收取每人99的费用作为直播和导师差旅。由于现场只能容纳30人的,因此我们正在联系专业的高清直播,会社区的小伙伴人提供直播活动,直播我们也需要收取一些费用来支持直播费用,具体费用等待后续的直播信息,后续的直播信息也会在公众号里推送,记得关注我的公众号-dotnet跨平台上关于中国.NET开发者峰会的信息。
相关文章
JavaScript的数据类型,共有六种 number(数值):整数和小数(比如1和3.14) string(字符串):文本(比如Hello World)。 boolean(布尔值):表示真伪的两个特殊值,即true(真)和false(假) undefined:表示“未定义”或不存在,即由于目前没有 定义,所以此处暂时没有任何值 null:表示空值,即此处的值为空。 object(对象):各种值组成的集合。
ES6 又新增了第七种 Symbol 类型的值
遇到在URL中拼接中文的参数,后台拿到的数据为乱码的问题。
解决的方法是在客户端对这个中文参数进行编码,然后服务端再进行解码就行了。
客户端编码(JavaScript)
var url = "contract!select.action?chineseParam=" + encodeURI(encodeURI("我是中文参数"));
注意:编码的时候需要使用两次encodeURI()方法,写一个就是????号,写两个则输出: %4d%5a这种。
服务端解码(Java)
String chineseParam = java.net.URLDecoder.decode(chineseParam, "UTF-8");
这样就能实现中文参数的前后端传递了。
另外要注意的是,这种方式只有在拼接URL参数的场景下有效,如果是将参数传递放在请求体中,比如AJAX中的data,是不需要对中文参数进行手动编码和解码操作的,只需要保证前后端的编码配置一致即可。
"人生最遗憾的,莫过于轻易地放弃了不该放弃的,却固执地坚持了不该坚持的。"
(以下内容亲手完成,如果需要搬走记得把写博的小白的名字和邮箱一起搬走)
出来玩(学习),总是要还的!
有不明的问题的时候,都来博客园转转,总能找到答案或者灵感,开博3个月都没发一篇帖(不晓得管理员有何感想,不会封我的号吧),不能只是索取没有付出。小白一枚琢磨了半天才扒拉明白Telnet服务搭建(照葫芦画瓢,也要知道葫芦从哪里来的),去繁就简,简单整理一下,分享一下。
Linux上的ssh那么好用为什么还要用Telnet这么老旧的东东呢? 最近被SSH 暴力枚举漏洞弄得头疼,奈何CentOS7最后版本是7.7(里面只openssh7.4,想升级到openssh 8.0),用yum升级ssh是没戏了,rpm的依赖关系(擦汗)。。。。。。,所以只能学编译安装了,第一次使用编译安装这种神器,真怕失手把sshd整挂了,弄个备胎,Telnet是也!
正文:
Setup 1 系统信息 安装
贴下系统信息 [图1]
[root@azeroth ~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
下面是已经安装完成的Telnet版本查询,如果还没有安装的是没有包信息显示的。[图2]
[root@azeroth ~]# rpm -qa|grep telnet
telnet-server-0.17-64.el7.x86_64
telnet-0.17-64.el7.x86_64
[root@azeroth ~]# rpm -qa|grep xinetd
xinetd-2.3.15-13.el7.x86_64
Telnet 远程登陆工具,Windows里面常常用来测试端口用(- .- !),Xinetd 第一次看见,百科里说是监视网络需求的守护进程(不晓得除了telnet以外还有什么网络服务会用这个?求教) ,等下有个叫telnet的配置文件要写在这里面。
安装
果断YUM哇!(呵呵,没有网?ISO,光驱这两样总得有一样吧,手工挂载制作CentOS-Media.repo源)[图3]
[root@azeroth ~]# yum install telnet telnet-server xinetd -y
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* c7-media:
......
执行完就可以查看到 [图2] 的结果了 。
Setup 2 Telnet进程配置
配置 (重点来了)
安装完以上会在 /etc/xinetd.d/ 目录下生成很多配置文件,ls 看是否有一个名为 telnet 的配置文件,尝试过两台设备 一台安装完会出现 telnet的默认配置,一台则没有,具体原因不详,暂时没查明原因,不过这个不重要可以参看 百科 xinetd制作一个,也可以copy一个。[图 4]
[root@azeroth ~]# ll /etc/xinetd.d/telnet
-rw-r--r--. 1 root root 342 Oct 21 21:21 /etc/xinetd.d/telnet
[root@azeroth ~]# cat /etc/xinetd.d/telnet
# default: on
# # description: The telnet server serves telnet sessions; it uses # # unencrypted username/password pairs for authentication.
# service telnet
# {
# disable = yes
# flags = REUSE
# socket_type = stream
# wait = no
# user = root
# server = /usr/sbin/in.telnetd
# log_on_failure += USERID
# }
配置解释(解释不对的地方,请大神指点):
存在此配置文件的情况下, 无需修改,是可以正常使用普通用户登陆Telnet服务器的亲测。
说明部分:默认情况下telnet服务为开启, telnet 服务器为 telnet 会话提供服务(废话),它使用未加密的用户名/密码对进行身份验证(试了一下创建一个没有分配密码的用户也没登陆上去)。
{ }部分
# disable = yes //我理解的意思大概是说 { } 内的字段默认情况下是不被使用的(这个理解似乎有问题,原文:”用在默认的 {} 中 禁止服务“,希望有大神指引一下)
# flags = REUSE //没有理解这里的意思是什么,请大神帮忙解答 “标识 = 可重复使用” ?
# socket_type = stream // 网络套接字类型
# user = root //使用root用户运行服务
# server = /usr/sbin/in.telnetd //执行进程路径
# log_on_failure += USERID //登陆失败日志
注:# disable = yes 此字段为 yes 或者 no 都不是决定能否使用root登陆的条件(此处被度娘搜索到的帖子误导了)
其实,Setup3 说了这么多,做了很多次实验和测试,此配置文件和是否能使用root登陆Telnet并没有直接的关系(决定是否能使用root登陆和另外一个配置有关系下文 叙述Setup 6),此配置文件理解的更多的是和Telnet的服务进程有关,哪么问题来了,此配置文件有存在的意义吗?当然,Xinet是用来监视守护网络进程的,Telnet是被Xinetd监视守护的对象,类似于监听的意思,但是比监听功能更强,如上的配置就是用作如何监视,用什么权限监视的配置。
Setup3 可以启动了
完成上面的安装和Xinetd配置检查,接下来就该添加自启动和运行服务了 [图 5 图 6 ]
[root@azeroth ~]# systemctl enable xinetd.service
[root@azeroth ~]# systemctl start xinetd.service
[root@azeroth ~]# systemctl status xinetd.service
● xinetd.service - Xinetd A Powerful Replacement For Inetd
Loaded: loaded (/usr/lib/systemd/system/xinetd.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2019-10-22 22:07:22 CST; 4min 29s ago
Main PID: 6883 (xinetd)
CGroup: /system.slice/xinetd.service
└─6883 /usr/sbin/xinetd -stayalive -pidfile /var/run/xinetd.pid
Oct 22 22:07:22 azeroth systemd[1]: Started Xinetd A Powerful Replacement For Inetd.
Oct 22 22:07:22 azeroth xinetd[6883]: removing discard
Oct 22 22:07:22 azeroth xinetd[6883]: removing discard
Oct 22 22:07:22 azeroth xinetd[6883]: removing echo
Oct 22 22:07:22 azeroth xinetd[6883]: removing echo
Oct 22 22:07:22 azeroth xinetd[6883]: removing tcpmux
Oct 22 22:07:22 azeroth xinetd[6883]: removing time
Oct 22 22:07:22 azeroth xinetd[6883]: removing time
Oct 22 22:07:22 azeroth xinetd[6883]: xinetd Version 2.3.15 started with libwrap loadavg labeled-networking options compiled in.
Oct 22 22:07:22 azeroth xinetd[6883]: Started working: 0 available services
[root@azeroth ~]# systemctl enable telnet.socket
[root@azeroth ~]# systemctl start telnet.socket
[root@azeroth ~]# systemctl status telnet.socket
● telnet.socket - Telnet Server Activation Socket
Loaded: loaded (/usr/lib/systemd/system/telnet.socket; enabled; vendor preset: disabled)
Active: active (listening) since Tue 2019-10-22 22:07:06 CST; 6min ago
Docs: man:telnetd(8)
Listen: [::]:23 (Stream)
Accepted: 1; Connected: 0
Oct 22 22:07:06 azeroth systemd[1]: Listening on Telnet Server Activation Socket.
Setup 4 防火墙放行
最容易感觉到的却也是最容易被遗忘的,捣鼓了半天咋还不能登陆,防火墙忘记放行啦!什么 Selinux还没关闭呢?(很多配置Telnet的帖子都提到需要关闭Selinux,不知道是何用意)不存在的,Firewall 放行了还需要关闭Selinux嘛?,亲测是不需要关闭的,Selinux安全上下文毕竟是Redhat系列系统的安全防护重点,不论配置什么应用建议都不要随意关闭,毕竟安全问题不能忽略。[图 7 ]
[root@azeroth ~]# firewall-cmd --add-port=23/tcp --permanent
success
[root@azeroth ~]# firewall-cmd --reload
success
Setup 5 完成
完成以上步骤,基本实现了Telnet服务的搭建,快到CMD里面使用Telnet 连接一下试试看吧!请使用普通用户,root权限还没开 ,如果不行请按照步骤检查或reboot。[图 8]
[root@azeroth ~]# telnet 192.168.11.130
Trying 192.168.11.130...
Connected to 192.168.11.130.
Escape character is ‘^]‘.
Kernel 3.10.0-957.5.1.el7.x86_64 on an x86_64
azeroth login: zym
Password:
Last login: Tue Oct 22 20:09:08 from ::ffff:192.168.11.1
[zym@azeroth ~]$
Setup 6 Root权限登陆配置
(不建议开启root权限,以上使用普通用户登陆之后即可su切换root了,一省事,二安全,两全其美)如果需要使用root权限登陆Telnet,还需要配置 /etc/securetty,将root允许使用telnet登陆的pts字段添加进配置文件。[图 9 图10]
[root@azeroth ~]# echo ‘pts/0‘ >>/etc/securetty
[root@azeroth ~]# echo ‘pts/1‘ >>/etc/securetty
[root@azeroth ~]# tail -f /etc/securetty
hvc4
hvc5
hvc6
hvc7
hvsi0
hvsi1
hvsi2
xvc0
pts/0
pts/1
[root@azeroth ~]# systemctl restart telnet.socket
以上关于CentOS7.6 系统Telnet服务的配置就全部介绍完了。感谢!
第一次发表随笔博文有点捉襟见肘,写博文一是让自己学习的更扎实;二是希望能有路过看见的大神或者和我一样的小白切磋一二,相互学习指引;技术是用来共享造福人类的。而不是闭门造车,也不只是用来挣钱的工具,这不该是信息共享的本意;三是在网上看见很多的帖子要么叙述不完整,要么有坑,如果有需要的和我一样的小白可以搜索到,这样就能少走一点点弯路节约时间。
—— ZhangXixi(原创) 2019-10-22 huayue_@hotmail.com
upstream my_nodejs_upstream {
server 127.0.0.1:3001;
keepalive 64;
}
server {
listen 80;
server_name www.my-website.com;
#ssl_certificate_key /etc/ssl/main.key;
#ssl_certificate /etc/ssl/main.crt;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://my_nodejs_upstream/;
proxy_redirect off;
proxy_read_timeout 240s;
}
}
摘自:
1、css,主页的图片可以自己找
<style>
.breadcrumbs {
height: 36px;
line-height: 36px;
color: #666;
font-size: 12px;
font-family: simsun;
padding-left: 20px;
background: url("${ctx}/base/system/zhqyConvergence/img/ho.png") left 10px no-repeat;
}
.breadcrumbs a {
color: #666;
text-decoration: none;
}
.breadcrumbs a:hover {
color: #f60;
}
.breadcrumbs span {
margin: 0 10px;
}
</style>
2、jsp
<div class="breadcrumbs">
<a href="https://www.gxlsystem.com#">主页<span>></span></a>
<a href="https://www.gxlsystem.com#">主页<span>></span></a>
<a href="https://www.gxlsystem.com#">主页<span>></span></a>
</div>
2、效果图
原文链接:https://blog.csdn.net/qq_38762313/article/details/85234594
全局异常拦截器:
解决写每个接口都需要去做容错而添加try{}catch{},有了该异常拦截器后,所有接口都不需要去添加异常处理。
实现方式:
第一步:新建一个类继承 IExceptionFilter,添加 using Microsoft.AspNetCore.Mvc.Filters;该类的名称命名要求是:后缀必须ExceptionFilter,例如:GlobalExceptionFilter。该类必须实现OnException 这个方法。这个方法是当异常发生时会进入。 例如下面的例子:
public class ExceptionFilter : IExceptionFilter
{
/// <summary>
/// 发生异常时进入
/// </summary>
/// <param name="context"></param>
public void OnException(ExceptionContext context)
{
if (context.ExceptionHandled == false)
{
context.Result = new ContentResult
{
Content = context.Exception.Message,//这里是把异常抛出。也可以不抛出。
StatusCode = StatusCodes.Status200OK,
ContentType = "text/html;charset=utf-8"
};
}
context.ExceptionHandled = true;
}
/// <summary>
/// 异步发生异常时进入
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public Task OnExceptionAsync(ExceptionContext context)
{
OnException(context);
return Task.CompletedTask;
}
}
第二步:修改Stup.cs文件如下即完成。
.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1) 把这段代码修改为:
.AddMvc(options =>
{
options.Filters.Add<GlobalExceptionFilter>();
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
前置和后置拦截器:
解决接口入参数的合法性验证问题,以及对参数等前期的信息处理等。例如:访问的合法性等。
实现方式:
第一步:异常拦截器很相似,新建一个类继承 IActionFilter,添加 using Microsoft.AspNetCore.Mvc.Filters;该类的名称命名要求是:后缀必须ActionFilter,例如:GlobalActionFilter。该类必须实现OnActionExecuted和OnActionExecuting这两个方法。这个方法是当异常发生时会进入。 例如下面的例子:
public class GlobalActionFilter : IActionFilter
{
/// <summary>
/// Action 执行后拦截
/// </summary>
/// <param name="context"></param>
public void OnActionExecuted(ActionExecutedContext context)
{
}
/// <summary>
/// Action 执行前拦截[模型验证应该在此处先处理]
/// </summary>
/// <param name="context"></param>
public void OnActionExecuting(ActionExecutingContext context)
{
context.HttpContext.Response.Headers["Access-Control-Allow-Origin"] = "*";//解决拦截器添加后跨域不生效的问题
if (!context.ModelState.IsValid)//验证参数的合法性问题。返回错误信息
{
///模型有效性验证失败处理逻辑...比如将提示信息返回
StringBuilder stringBuilder = new StringBuilder();
///模型有效性验证失败处理逻辑.....
///如返回错误信息,这里可自动包装
foreach (var item in context.ModelState.Values)
{
foreach (var error in item.Errors)
{
stringBuilder.Append($"{ error.ErrorMessage}|");
}
}
context.Result = new ContentResult
{
Content = stringBuilder.ToString(),
StatusCode = StatusCodes.Status200OK,
ContentType = "text/html;charset=utf-8"
};
}
}
}
第二步:修改Stup.cs文件如下即完成。
.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1) 把这段代码修改为:
.AddMvc(options =>
{
options.Filters.Add<GlobalActionFilter>();
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
模型和参数验证的使用方式[配合前置拦截器,开发人员可以不用操心验证和返回错误信息]:
在参数前面添加特性,常用的三个特性如下
接收Query的写法
[HttpGet("test")]
public object test([FromQuery] [Required(ErrorMessage = "名称不能为空")][MaxLength(5,ErrorMessage = "名称长度不能大于5")][MinLength(3,ErrorMessage = "名称最小长度为3")] string name)
{
}
一个参数可以同时写多个特性进行验证。
接收Body的写法
该特性验证要在实体类上写,接参时用实体类去接,这样验证通用起到效果。
例如:
[HttpPost("test")]
public object test([FromBody] Users user)
{
}
public class Users
{
[Required(ErrorMessage = "名称不能为空")]
public string Name{get;set;};
public int Age{get;set;};
}
PHP 从诞生到现在已经有20多年历史,从Web时代兴起到移动互联网退潮,互联网领域各种编程语言和技术层出不穷, Node.js 、 GO 、 Python 不断地在挑战 PHP 的地位。这些技术的推动者非常热衷于唱衰 PHP , PHP 语言的未来在哪里?PHP 程序员当如何应对未来的变革?
作为老牌的Web后端编程语言,PHP 在全球市场占有率非常高,仅次于 Java ,从各个招聘网站的数据上来看PHP 开发的职位非常多,薪资水平也非常不错。实际在中小企业、互联网创业公司PHP的市场地位是高于 Java 的。Java 在超大型企业、传统软件行业、金融领域优势更大。目前来看 Node.js、 GO 、 Python 、 Ruby 等语言还难以企及PHP和Java。
PHP 语言之所以能有今天的地位,得益于PHP语言设计者一直遵从实用主义,将技术的复杂性隐藏在底层。PHP 语言入门简单,容易掌握,程序健壮性好,不容易出现像 Java 、 C++ 等其他语言那样复杂的问题,如内存泄漏和 Crash ,跟踪调试相对轻松很多。PHP 官方提供的标准库非常强大,各种功能函数都能在官方的标准库中找到,包括MySQL、Memcache、Redis、GD图形库、CURL、XML、JSON等等,免除了开发者到处找库的烦恼。PHP 的文档非常棒,每个函数都有详细的说明和使用示例。第三方类库和工具、代码、项目也很丰富。开发者可以快速、高效地使用 PHP 编写开发各类软件。到目前为止市面上仍然没有出现比 PHP 更简单易用的编程语言。所以 PHP 的前景还是很广阔的,与其纠结于编程语言的选择,不如好好地深入学习使用 PHP 。
作为一个资深的 PHP 开发者,在技术上给各位 PHP 程序十点未来的建议,希望对大家有所帮助。
1、Composer
第一点就要提 Composer ,自从 Composer 出现后,PHP 的依赖管理可以变得非常简单。程序内依赖一些类库和框架,直接使用 Composer 引入即可,通过使用 composer update 安装依赖的包。解决了过去加载外部库的各种难题。Composer 也有国内镜像,速度非常快。现在绝大部分PHP开源的项目都提供了 Composer 的支持,建议大家在项目中使用 Composer 来解决 PHP 代码包管理的问题,不要再使用下载源码、手工 include 的原始方法。
2、PHP7
PHP7 版本对 Zend 引擎做了大量修改,大幅提升了 PHP 语言的性能,使用 PHP7 可以使你的程序性能瞬间翻倍。即使是 WordPress 这样重量级的软件运行在 PHP7 都能有上千 QPS ,相当于一台服务器每天就能处理 8000 万次请求。使用 PHP7 ,做好 MySQL 优化,使用 Memcache 和 Redis 进行加速,这套技术架构完全可以应对相当大规模的系统。除了某些亿级用户的平台之外,一般规模的系统完全没有压力。
3、PSR
PSR 是 http://www.phpfig.org/ 组织制定的PHP语言开发规范,约定了很多方面的规则,如命名空间、类名
规范、编码风格标准、Autoload、公共接口等。现在已经成为PHP技术社区事实上的标准了。很多知名的 PHP 框架和类库都遵守了 PSR 规范。PHP 开发者应当学习掌握 PSR 规范,在开发程序时应当尽量遵循 PSR 规范。
4、Swoole
2017 年 PHP 还局限于做 Web 网站吗?No ,如果你还不知道 Swoole ,赶快去了解一下吧。Swoole 的口号是重新定义 PHP 语言,Swoole 是一个异步并行的通信引擎,作为 PHP 的扩展来运行。Node.js 的异步回调 Swoole 有,Go语言的协程 Swoole 也有,这完全颠覆了对 PHP 的认知。使用 Swoole PHP 可以实现常驻内存的 Server 程序,可以实现 TCP 、 UDP 异步网络通信的编程开发。过去PHP只能做一个 Web 网站,现在使用 Swoole 可以做 Java 、C++ 才能实现的通信服务,比如 WebSocket 即使通信、聊天、推送服务器、RPC 远程调用服务、网关、代理、游戏服务器等。如果你想用 PHP 做点 Web 系统之外的东西,Swoole 是最好的选择。
5、Laravel
最近几年最火热的 PHP 框架,官网号称是为 Web 艺术家设计的框架,可见这套框架有多优雅。Laravel 提供的功能模块丰富,API 设计简洁,表达力强。而且它的社区非常活跃,代码贡献者众多,第三方的插件非常多,生态系统相当繁荣。 Laravel 底层使用了很多 symfony2 组件,通过 composer 实现了依赖管理。如果还在纠结使用什么PHP框架,不如选择 Laravel 。 Laravel 提供的命令行工具基于 symfony.console 实现,功能强大,集成了各种项目管理、自动生成代码的功能。
6、Phar
PHP5.3 之后支持了类似 Java 的 jar 包,名为 phar。用来将多个 PHP 文件打包为一个文件。这个特性使得 PHP 也可以像 Java 一样方便地实现应用程序打包和组件化。一个应用程序可以打成一个 Phar 包,直接放到
PHPFPM 中运行。配合 Swoole ,可以在命令行下执行 php server.phar 一键启动服务器。PHP 的代码包可以用 Phar 打包成组件,放到 Swoole 的服务器容器中去加载执行。
7、C/C++/GO
任何技术有优点就有缺点,PHP 作为一门动态脚本语言,优点是开发方便效率高。缺点就是性能差。在密集运算的场景下比 C 、 C++ 相差几十倍甚至上百倍。另外 PHP 不可以直接操作底层,需要依赖扩展库来提供 API 实现。PHP 程序员可以学习一门静态编译语言作为补充实现动静互补,C/C++/Go 都是不错的选择。而且静态语言的编程体验与动态语言完全不同,学习过程可以让你得到更大的提升。
掌握 C/C++ 语言后,还可以阅读 PHP 、 Swoole 、 Nginx 、Redis 、 Linux内核 等开源软件的源码,了解其底层运行原理。
现在最新版本的Swoole提供了C++扩展模块的支持,封装了Zend API,用C++操作PHP变得很简单,可以用C++实现PHP扩展函数和类。
8、HTML5
作为 Web 前端新一代标准,HTML5 未来前景非常广阔,市场需求量非常大。从 PC 网站、B/S 企业软件、移动端网页、APP,这些领域都在拥抱 HTML5,掌握了 HTML5 才能在下一波互联网技术大潮中存活下来。
9、 Vue.js
PHP 程序员除了写后台程序之外,还有很大一部分工作在展现层,和浏览器前端打交道。2017 年你还在用 jQuery 操作 DOM 实现界面渲染吗?已经完全 out 了。现在用 Vue.js 可以非常方便地实现数据和 DOM 元素的绑定。通过 Ajax 请求后台接口返回数据后,更新前端数据自动实现界面渲染。2017 年再不学 Vue 就晚了。
如果你不光要写 Web 程序,同时还希望兼顾 Android 、IOS 、PC 客户端等平台,React Native 是一个不错的选择。
10、深度学习/人工智能
互联网的未来属于人工智能,如果你还不了解机器学习、深度学习、人工智能这些概念,那你需要尽快学习了解一下。现在互联网巨头们都在布局人工智能,包括 Google 、 Facebook 、微软、亚马逊 和国内的百度。虽然现在还处于科学研究的阶段,但未来互联网的各个领域都会应用到人工智能,包括自动驾驶、大数据分析、网络游戏、图像识别、语言处理等。当然现在普通的工程师可能还无法参与到人工智能产品中,但至少应该理解深度学习/人工智能的基本概念和原理。
链接: 提取码:x2p5
免费分享,但是X度限制严重,如若链接失效点击链接或搜索加群 群号。
setInterval(): 间隔指定的毫秒数不停地执行指定的代码,定时器
clearInterval(): 用于停止 setInterval() 方法执行的函数代码
使用方法:
setInterval(code,millisec),两个参数都是必须的,第一个参数为要调用的函数或要执行的代码串。第二个参数为周期性执行或调用 code 之间的时间间隔,以毫秒计。
clearInterval(id_of_setinterval),参数是必须的,为setInterval返回的ID值
示例:
<body>
<button οnclick="start()">按钮一</button>
<button οnclick="stop()">按钮二</button>
<script type="text/javascript">
var interval = null;//计时器
var i = 0;
function start(){//启动计时器函数
if(interval!=null){//判断计时器是否为空
clearInterval(interval);
interval=null;
}
interval = setInterval(overs,1000);//启动计时器,调用overs函数,
}
function overs(){
i++;
console.log(i);
}
function stop(){
clearInterval(interval);
interval = null;
}
</script>
</body>
在使用setInterval方法时,每一次启动都需要对setInterval方法返回的值做一个判断,判断是否是空值,若不是空值,则要停止定时器并将值设为空,再重新启动,如果不进行判断并赋值,有可能会造成计时器循环调用,在同等的时间内同时执行调用的代码,并会随着代码的运行时间增加而增加,导致功能无法实现,甚至占用过多资源而卡死奔溃。因此在每一次使用setInterval方法时,都需要进行一次判断。
<body>
<button οnclick="start()">按钮一</button>
<button οnclick="stop()">按钮二</button>
<script type="text/javascript">
var interval = null;//计时器
var i = 0;
function start(){//启动计时器函数
if(interval!=null){//判断计时器是否为空
clearInterval(interval);
interval=null;
}
interval = setInterval(overs,1000);//启动计时器,调用overs函数,
}
function overs(){
i++;
console.log(i);
}
function stop(){
clearInterval(interval);
interval = null;
}
</script>
</body>
Dim excelApplication AsNew Excel.Application
Dim excelWorkBook As Excel.Workbook = excelApplication.Workbooks.Add()
Dim excelWorkSheet As Excel.Worksheet = excelWorkBook.Worksheets.Add
Dim cellStart = excelWorkSheet.Cells(2, 3)
Dim cellEnd = excelWorkSheet.Cells(10, 20)
With excelWorkSheet.Range(cellStart, cellEnd).Interior
‘.Pattern = Microsoft.Office.Interop.Excel.Constants.xlSolid
‘.PatternColorIndex = Microsoft.Office.Interop.Excel.Constants.xlAutomatic
.Color =Color.Yellow
‘.TintAndShade = 0
‘.PatternTintAndShade = 0
EndWith
解决node.js使用fs读取文件出错:
今天配接口,使用fs模块读取json出现了错误‘no such file or directory‘,然后经查终于解决,特此记录。
使用nodejs的fs模块读取文件时习惯用相对路径,但是运行的时候出现了上述的错误,原因就是fs模块读取文件的相对路径是以启动server.js的位置为基准的,而不是以server.js文件的位置。
这就是这篇文章所要讲述的问题。并且nodejs官方推荐在使用fs模块读取文件时使用绝对路径,而不是相对路径。
但是写绝对路径又有些许麻烦,那该如何解决呢,参考以下代码就可以啦:
var fs = require(‘fs‘);
let path = require(‘path‘);
let PUBLIC_PATH = path.resolve(__dirname, ‘xx.json‘);
fs.readFile(PUBLIC_PATH, ‘utf8‘, function (err, data) {
if (err) console.log(err);
});
前言
AutoEncoder是深度学习的另外一个重要内容,并且非常有意思,神经网络通过大量数据集,进行end-to-end的训练,不断提高其准确率,而AutoEncoder通过设计encode和decode过程使输入和输出越来越接近,是一种无监督学习过程。
AutoEncoder
Introduction
AutoEncoder包括两个过程:encode和decode,输入图片通过encode进行处理,得到code,再经过decode处理得到输出,有趣的是,我们控制encode的输出维数,就相当于强迫encode过程以低维参数学习高维特征,这导致的结果和PCA类似。
AutoEncoder的目的是使下图中的输入x和输出x_head越相似越好,这就需要在每次输出之后,进行误差反向传播,不断优化。
高维数据对于我们的感官体验总是不友好,如果我们将输入降低至二维,放在二维平面中就会更加直观,下图是MNIST数据集做AutoEncoder:
上面是PCA的结果,下面是AutoEncoder的结果,在二维中结果很清晰。
encode和decode两个过程可以理解成互为反函数,在encode过程不断降维,在decode过程提高维度。当AutoEncoder过程中用卷积操作提取特征,相当于encode过程为一个深度卷积神经网络,好多层的卷积池化,那么decode过程就需要进行反卷积和反池化,那么,反卷积和反池化如何定义呢?
Unpooling
池化过程实际上就是降维过程,假设图片大小为32x32,池化大小为2x2,就相当于将图片中相邻的2x2个像素点替换为四个点中最大数值(max-pooling),池化处理之后得到的图片大小为16x16,Unpooling过程则需要将16x16的图片变为32x32,其实在池化过程中,会标记2x2像素点中最大值的位置,在Unpooling过程将最大值还原,其他位置填0。
以上并不是Unpooling的唯一做法,在Keras中,不会记住最大值的位置,而是将所有像素均以最大值填充。
Deconvolution
卷积过程是一个矩阵在另一个矩阵上面做滑动运算,反卷积也是一样,实际上,反卷积就是卷积,看下面的图,我们熟悉的是左面的卷积过程,假设有5个像素点,卷积核为3,步长为1,卷积之后生成3个feature,我们想象中的反卷积应该是中间所示的情形,由3个输入生成5个输出,如果我们将反卷积中的输入做2的padding,这样原本3个输入变成7个输入,再做卷积,生成5个输出,对比左右两侧的图,是完全相反的,所以,我们加上padding,使反卷积变成了卷积运算。
De-noising AutoEncoder
对于AutoEncoder,每一次的训练都是自身的对比,这回造成输出和输入越来越类似,而对同种类的其他图片表现不敏感,于是,De-noising AutoEncoder派上了用场,如下图所示,在输入之前,先将图片加入随机噪声,这样每次的输入都会略有差异,然后将带有噪声的图片进行AutoEncoder,将输出的y与加噪声之前的图片进行比较,这样训练出来的y就具有抗噪声的能力,在以图搜图的场景下也就提高了泛化能力。
算法推导
参考资料
李航《统计学习方法》
前提是在码云上已经新建一个空的项目
1、新建一个目录,存放下载下来的项目,我在D盘新建了一个“gitspace”文件夹,用来存放下载下来的项目
2、进入刚刚新建的文件夹,即进入“gitspace”,点击鼠标右键,选择"Git Bash Here",如下图:
点击“Git Bash Here”之后,可以看到下面界面,否则,可能是你的Git Bash安装有问题
3、进行基础配置,作为 git 的基础配置,作用是告诉 git 你是谁,你输入的信息将出现在你创建的提交中,使用下面两条命令:
git config --global user.name "你的名字或昵称"
git config --global user.email "你的邮箱"
4、在gitspace文件夹中执行下面命令,完成初始化
git init
git remote add origin <你的项目地址> //注:项目地址形式为:https://gitee.com/xxx/xxx.git或者 git@gitee.com:xxx/xxx.git
5、如果你想克隆,只需要执行命令
git clone <项目地址>
弹出窗口,输入码云的账户名、密码
点击“确定”
再看gitspace文件夹下,已经下载下来了
6、进入你已经初始化好的或者克隆项目的目录,然后执行:
从服务器下更新项目,因为已经clone过,所以不需要再更新
git pull origin master
7、做一些修改,比如添加一个"说明.txt"文件
执行下面命令,完成第一次提交
git add .
git commit -m “安装教程测试”
git push origin master
注意:提交命令有两个,git push origin master(正常提交)和git push origin master -f(强制提交,强制提交可能会把之前的commit注释信息,不会改变修改的代码,慎用),都是提交到master分支
这样就完成了使用git bash从码云上下载和上传代码。
参考链接:
目录
> 一、总结(点击显示或隐藏总结内容)
一句话总结:Scroll是一个类,每个需要使用滚动功能的区域均要进行初始化。
最佳的结构如下:
iScroll作用于滚动区域的外层。在上面的例子中,UL元素能进行滚动。只有容器元素的第一个子元素能进行滚动,其他子元素完全被忽略。
最基本的脚本初始化的方式如下:
二、iscroll.js的简单使用方法(总结)
原文链接:
这篇文章讲到了iscroll的一些使用入门、参数配置和示例,并讲到了滚动条的接口,自定义事件和销毁动作等。原文:
入门
Scroll是一个类,每个需要使用滚动功能的区域均要进行初始化。每个页面上的iScroll实例数目在设备的CPU和内存能承受的范围内是没有限制的。
尽可能保持DOM结构的简洁。iScroll使用硬件合成层但是有一个限制硬件可以处理的元素。
最佳的结构如下:
iScroll作用于滚动区域的外层。在上面的例子中,UL元素能进行滚动。只有容器元素的第一个子元素能进行滚动,其他子元素完全被忽略。
最基本的脚本初始化的方式如下:
**第一个参数可以是滚动容器元素的DOM选择器字符串**,**也可以是滚动容器元素的引用对象**。下面是一个有效的语法:
所以基本上你要么直接传递元素,要么传递一个querySelector字符串。因此可以使用css名称代替ID去选择一个滚动器容器,如下:
注意,iScroll使用的是querySelector 而不是 querySelectorAll,所以iScroll只会作用到选择器选中元素的第一个。如果你需要对多个对象使用iScroll,你需要构建自己的循环机制。
初始化
当DOM准备完成后iScroll需要被初始化。最保险的方式是在w**indow的onload事件中启动它**。
在**DOMContentLoaded**事件中或者**inline initialization**中做也可以,需要记住的是脚本需要知道滚动区域的高度和宽度。***如果你有一些图片在滚动区域导致不能立马获取区域的高度和宽度,iScroll的滚动尺寸有可能会错误***。
为滚动起容器增加position:relative或者absolute样式。这将解决大多数滚动器容器大小计算不正确的问题。
综上所述,最小的iScroll配置如下:
**注:**如果你有一个复杂的DOM结构,最好在onload事件之后适当的延迟,再去初始化iScroll。最好给浏览器100或者200毫秒的间隙再去初始化iScroll。
参数配置
在iScroll初始化阶段可以通过构造函数的第二个参数配置它。
上面的例子示例了在iScroll初始化时开启鼠标滚轮支持和滚动条支持。
理解核心
iScroll使用基于设备和浏览器性能的各种技术来进行滚动。通常不需要你来配置引擎,iScroll会为你选择最佳的方式。
尽管如此,理解iScroll工作机制和了解如何去配置他们也是很重要的。
options.useTransform
默认情况下引擎会使用**CSStransform**属性。如果现在还是2007年,那么可以设置这个属性为false,这就是说:引擎将使用top/left属性来进行滚动。
这个属性在滚动器感知到Flash,或者视频插件内容时会有用,但是需要注意:性能会有极大的损耗。
默认值:true
options.useTransition
iScroll使用** transition**来实现动画效果(动量和弹力)。如果设置为false,那么将使用requestAnimationFrame代替。
在现在浏览器中这两者之间的差异并不明显。在老的设备上transitions执行得更好。默认值:true
options.HWCompositing
这个选项尝试使用**translateZ(0)**来把滚动器附加到硬件层,以此来改变CSS属性。在移动设备上这将提高性能,但在有些情况下,你可能想要禁用它(特别是如果你有太多的元素和硬件性能跟不上)。
默认值:true
*注:***如果不确定iScroll的最优配置。从性能角度出发,上面的所有选项应该设置为true。(或者更好的方式,让他们自动设置属性为true)。你可以尝试这配置他们,但是要小心内存泄漏。
滚动条
滚动条不只是像名字所表达的意义一样,在内部它们是作为indicators的引用。
一个指示器侦听滚动条的位置并且现实它在全局中的位置,但是它可以做更多的事情。
先从最基本的开始。
options.scrollbars
正如我们在基本功能介绍中提到的,激活滚动条只需要做一件事情,这件事情就是:
当然这个默认的行为是可以定制的。
滚动的编程接口
当然还可以通过编程来进行滚动。
scrollTo(x, y, time, easing)
对应存在的一个叫做myScroll的iScroll实例,可以通过下面的方式滚动到任意的位置:
通过上面的方式将向下滚动100个像素。记住:0永远是左上角。需要滚动你必须传递负数。
time 和 easing是可选项。他们控制滚动周期(毫秒级别)和动画的擦除效果。
擦除功能是一个有效的IScroll.utils.ease对象。例如应用一个一秒的经典擦除动画你应该这么做:
擦除动画的类型选项有:quadratic, circular, back, bounce, elastic。
scrollBy(x, y, time, easing)
和上面一个方法类似,但是可以传递X和Y的值从当前位置进行滚动。
上面这个语句将在当前位置向下滚动10个像素。如果你当前所在位置为-100,那么滚动结束后位置为-110.
scrollToElement(el, time, offsetX, offsetY, easing)
这是一个很有用的方法,你会喜欢它的。
在这个方法中只有一个强制的参数就是el。传递一个元素或者一个选择器,iScroll将尝试滚动到这个元素的左上角位置。
time是可选项,用于设置动画周期。
offsetX 和 offsetY定义像素级的偏移量,所以你可以滚动到元素并且加上特别的偏移量。但并不仅限于此。如果把这两个参数设置为true,元素将会位于屏幕的中间。
easing参数和scrollTo方法里的一样。
对齐
iScroll能对齐到固定的位置和元素。
options.snap
最简单的对齐配置如下:
这将按照页面容器的大小自动分割滚动条。
snap属性也可以传递字符类型类型的值。这个值是滚动条将要对齐到的元素的选择器。比如下面:
这个示例中滚动条将会对齐到每一个LI标记的元素。
下面将帮助你快速浏览iScroll提供的关于对齐的一系列有趣的方法。
goToPage(x, y, time, easing)
x 和 y呈现你想滚动到横向轴或者纵向轴的页面数。如果你需要在单个唯独上使用滚动条,只需要为你不需要的轴向传递0值。
time属性是动画周期,easing属性是滚动到指定点使用的擦除功能类型。请参考高级功能中的option.bounceEasing。这两个属性都是可选项。
myScroll.goToPage(10, 0, 1000);
上面这个例子将在一秒内沿着横向滚动到第10页。
next()
prev()
滚动到当前位置的下一页或者前一页。
缩放
为了使用缩放功能,你最好使用iscroll-zoom.js脚本。
和前面的示例一样,一个好的缩放功能的配置如下:
刷新
iScroll需要知道包装器和滚动器确切的尺寸,在iScroll初始化的时候进行计算,如果元素大小发生了变化,需要告诉iScroll DOM发生了变化。
下面将提供调用refresh方法的正确时机。
每次触摸DOM,浏览器渲染器重绘页面。一旦发生了重画我们可以安全地读新的DOM属性。重新绘制阶段不是瞬时发生的只是范围结束时触发。这就是为什么我们需要给渲染器刷新iScroll之前一点时间。
为了确保javascript得到更新后的属性,应该像下面的例子这样使用刷新方法:
这里调用refresh()使用了零秒等待,如果你需要立即刷新iScroll边界就是如此使用。当然还有其他方法可以等待页面重绘,但零超时方式相当稳定。
**注:***如果你有一个相当复杂的HTML结构,你应该给浏览器更多的执行事件,可以设置100到200毫秒的超时时间。
这通常适用于所有任务必须在DOM上进行。通常给渲染器一些执行的时间。*
自定义事件
iScroll还提供额一些你可以挂靠的有用的自定义事件。
使用on(type, fn)方法注册事件。
上面的代码会在每次滚动停止是执行doSomething方法。
可以挂靠的事件如下:
beforeScrollStart,在用户触摸屏幕但还没有开始滚动时触发。
scrollCancel,滚动初始化完成,但没有执行。
scrollStart,开始滚动
scroll,内容滚动时触发,只有在scroll-probe.js版本中有效,请参考onScroll event。
scrollEnd,停止滚动时触发。
flick,用户打开左/右。
zoomStart,开始缩放。
zoomEnd,缩放结束。
按键绑定
你可以激活keyBindings选项来支持键盘控制。默认情况下iScroll监听方向键,上下翻页建,home/end键,但这些按键绑定完全可以自定义。
你可以通过传递一个包含按键代码列表的对象来进行按键绑定。
默认的按键值如下:
当然你也可以传递字符串进行按键绑定(例如:pageUp: ‘a‘)。只要你设置了对于的按键值,那么iScroll就会响应你的设置。
滚动条信息
iScroll存储了很多有用的信息,您可以使用它们来增强您的应用。
你可能会发现有用的:
myScroll.x/y,当前位置
myScroll.directionX/Y,最后的方向 (-1 down/right, 0 still, 1 up/left)
myScroll.currentPage,当前对齐捕获点
下面是关于处理时间的代码示例:
如果 x 位置是低于-1000 像素滚轮停止时,上述执行某些代码。请注意我用这个产品而不是 myScroll,您可以使用两个当然,但 iScroll 传递本身作为这种情况下,当触发自定义事件的功能。
销毁
在不需要使用iScoll的时候调用iScroll实例的公共方法destroy()可以释放一些内存。
参考:
通常在前端开发过程中,经常遇到需要绑定两个甚至多个元素之间的值,比如将input的值绑定到一个h1上,改变input的值,h1的文字也自动更新。
<h1 id="title">Hello</h1>
<input type="text" id="a" />
首先是在界面上更改input的值,需要监听input的“input”事件:
var input = document.getElementById("a"),
title = document.getElementById("title");
input.oninput = function (e) {
title.innerHTML = this.value;
};
如果是在代码中手动修改input.value属性,这时候最常规的方法是手动更新h1的文字:
input.value = ‘123‘;
title.innerHTML = input.value;
有没有办法能让我们在更新input的value值的时候,自动更新h1的文字呢?当然,我们需要用到Object.defineProperty方法:
Object.defineProperty(input, ‘val‘, { //这里必须定义一个新的属性名称,不可以用value,否则会报错。
get: function () {
return this.value;
},
set: function (val) {
this.value = val;
title.innerHTML = val;
}
});
这样我们就为input增加了一个val属性,val的值与input.value是关联的,并且我们在val的setter中增加了更新h1文字的代码,现在我们就可以直接通过
input.val = ‘good‘;
的方式来同时更新input.value和h1的文字了
用Object.defineProperty这个方法来实现数据的双向绑定是Vue的核心思想,该方法的更多细节请看
目录
1、glusterfs概述
1.1、glusterfs简介
glusterfs是一个可扩展,分布式文件系统,集成来自多台服务器上的磁盘存储资源到单一全局命名空间,以提供共享文件存储。
1.2、glusterfs特点
可以扩展到几PB容量
支持处理数千个客户端
兼容POSIX接口
使用通用硬件,普通服务器即可构建
能够使用支持扩展属性的文件系统,例如ext4,XFS
支持工业标准的协议,例如NFS,SMB
提供很多高级功能,例如副本,配额,跨地域复制,快照以及bitrot检测
支持根据不同工作负载进行调优
1.3、glusterfs卷的模式
中的的模式有很多中,包括以下几种:
分布卷(默认模式):即DHT, 也叫 分布卷: 将文件以hash算法随机分布到 一台服务器节点中存储。
复制模式:即AFR, 创建volume 时带 replica x 数量: 将文件复制到 replica x 个节点中。
条带模式:即Striped, 创建volume 时带 stripe x 数量: 将文件切割成数据块,分别存储到 stripe x 个节点中 ( 类似raid 0 )。
分布式条带模式:最少需要4台服务器才能创建。 创建volume 时 stripe 2 server = 4 个节点: 是DHT 与 Striped 的组合型。
分布式复制模式:最少需要4台服务器才能创建。 创建volume 时 replica 2 server = 4 个节点:是DHT 与 AFR 的组合型。
条带复制卷模式:最少需要4台服务器才能创建。 创建volume 时 stripe 2 replica 2 server = 4 个节点: 是 Striped 与 AFR 的组合型。
三种模式混合: 至少需要8台 服务器才能创建。 stripe 2 replica 2 , 每4个节点 组成一个 组。
2、heketi概述
是一个提供管理卷的框架,能够在、、等云平台上实现动态的存储资源供应,支持多集群管理,便于管理员对进行操作,在集群中,将存储的请求发送至,然后控制集群创建对应的存储卷。
动态在集群内选择构建指定的,以确保副本会分散到集群不同的故障域内。
还支持任意数量的集群,以保证接入的云服务器不局限于单个集群。
3、部署heketi+glusterfs
环境:安装的最新版本,由1master+2node组成,网络插件选用的是,默认安装的,会给打上污点,本文为了实现集群功能,先手动去掉了污点。
本文的卷模式为复制卷模式。
另外,在集群中需要以特权运行,需要在中添加参数以开启此功能,默认此版本的已开启。
3.1、准备工作
为了保证能够正常使用作为后端存储,需要每台运行的节点上提前安装的客户端工具,其他存储方式也类似。
3.1.1、所有节点安装glusterfs客户端
3.1.2、节点打标签
需要安装的设置,因为是通过集群的方式安装的。
安装方式默认会在每个节点上都进行安装,除非安装前设置筛选要安装节点,带上此标签的节点才会安装。
安装脚本中设置中设置安装在贴有 的节点,所以这是事先将节点贴上对应。
3.1.3、所有节点加载对应模块
查看是否加载
3.2、创建glusterfs集群
采用容器化方式部署集群,同样也可以使用传统方式部署,在生产环境中,集群最好是独立于集群之外进行部署,之后只需要创建对应的即可。这里采用方式部署,同时保证已经打上标签的节点上都运行一个服务,并且均有提供存储的磁盘。
3.2.1、下载相关安装文件
在本集群中,下面用到的控制器及后面用到的控制器的版本均变为了,所以需要手动修改下载的文件再进行部署,资源编排文件中需要指定声明。避免出现以下报错:
修改版本
为
指定声明
对应后面内容的r,用相关联
3.2.2、创建集群
注意:
这里使用的是默认的挂载方式,可使用其他磁盘作为的工作目录
此处创建的为,可手动指定为其他
3.2.3、查看gfs pods
3.3、创建heketi服务
3.3.1、创建heketi的service account对象
3.3.2、创建heketi对应的权限和secret
3.3.3、初始化部署heketi
同样的,需要修改版本以及增加声明部分。
3.4、创建gfs集群
3.4.1、复制二进制文件
复制到目录下
3.4.2、配置topology-sample
修改,为管理服务的节点主机名,为节点的地址,为节点上的裸设备,也就是用于提供存储的磁盘最好使用裸设备,不进行分区。
因此,需要预先在每个的节点上准备好新的磁盘,这里分别在三个节点都新添加了一块/dev/sdb磁盘设备,大小均为10G。
配置
3.4.3、获取当前heketi的ClusterIP
查看当前的,并通过环境变量声明
3.4.4、使用heketi创建gfs集群
执行如下命令创建gfs集群会提示Invalid JWT token: Token missing iss claim
这是因为新版本的heketi在创建gfs集群时需要带上参数,声明用户名及密码,相应值在heketi.json文件中配置,即:
执行了heketi-cli topology load之后,Heketi在服务器做的大致操作如下:
进入任意glusterfs Pod内,执行gluster peer status 发现都已把对端加入到了可信存储池(TSP)中。
在运行了gluster Pod的节点上,自动创建了一个VG,此VG正是由topology-sample.json 文件中的磁盘裸设备创建而来。
一块磁盘设备创建出一个VG,以后创建的PVC,即从此VG里划分的LV。
heketi-cli topology info 查看拓扑结构,显示出每个磁盘设备的ID,对应VG的ID,总空间、已用空间、空余空间等信息。
通过部分日志查看
3.4.5、持久化heketi配置
上面创建的heketi没有配置持久化的卷,如果heketi的pod重启,可能会丢失之前的配置信息,所以现在创建heketi持久化的卷来对heketi数据进行持久化,该持久化方式利用gfs提供的动态存储,也可以采用其他方式进行持久化。
在所有节点安装device-mapper*
将配置信息保存为文件,并创建持久化相关信息
删除中间产物
创建持久化的heketi
查看持久化后heketi的svc,并重新声明环境变量
查看gfs集群信息,更多操作参照官方文档说明
4、创建storageclass
参数说明:
reclaimPolicy:Retain 回收策略,默认是Delete,删除pvc后pv及后端创建的volume、brick(lvm)不会被删除。
gidMin和gidMax,能够使用的最小和最大gid
volumetype:卷类型及个数,这里使用的是复制卷,个数必须大于1
5、测试通过gfs提供动态存储
创建一个使用动态,在指定之前创建的,即:
创建pod并查看创建的pv和pvc
6、分析k8s通过heketi创建pv及pvc的过程
通过及向申请创建对应的,具体可通过查看创建的的日志
首先发现接收到请求之后运行了一个任务,创建了三个,在三个节点中创建对应的目录:
创建lv,添加自动挂载
创建brick,设置权限
创建对应的volume
7、测试数据
测试使用该的之间能否共享数据,手动进入到并创建文件
查看创建的卷
将设备挂载查看卷中的数据,vol_08e8447256de2598952dcb240e615d0f为卷名称
8、测试deployment
测试通过控制器部署能否正常使用,创建的
查看相应资源
查看挂载情况
在宿主机挂载和创建文件
扩容nginx副本,查看是否能正常挂载
至此,在集群中部署提供动态存储结束。
参考来源:
https://github.com/heketi/heketi
https://github.com/gluster/gluster-kubernetes
https://www.cnblogs.com/jicki/p/5801712.html
项目要求通过网站上传大文件,比如视频文件,通过摸索实现了文件分片来上传,然后后台进行合并。
使用了开源的前台上传插件WebUploader(http://fex.baidu.com/webuploader/)
WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,iOS 6+, android 4+。两套运行时,同样的调用方式,可供用户任意选用。
采用大文件分片并发上传,极大的提高了文件上传效率。
直接上代码,前台cshtml
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<link href="https://www.gxlsystem.com/~/js/webuploader/webuploader.css" rel="stylesheet" />
<link href="https://www.gxlsystem.com/~/js/bootstrap.min.css" rel="stylesheet" />
<script src="https://www.gxlsystem.com/~/js/jquery.min.js"></script>
<script src="https://www.gxlsystem.com/~/js/webuploader/webuploader.js"></script>
<title>Upload</title>
<script>
jQuery(function () {
var $ = jQuery,
$list = $(‘#thelist‘),
$btn = $(‘#ctlBtn‘),
state = ‘pending‘,
fileMd5,
flag = true,
dataState,
fm = [],
fnum,
Token,
uploader;
var FileExt = ["mpg", "mpeg", "mp4", "avi"];
Token = ‘@ViewBag.Token‘;
if (Token == ‘‘ || Token== ‘undefined‘)
{
$("#uploader").hide();
alert("登录超时,请重新登录。");
}
//监听分块上传过程中的三个时间点
WebUploader.Uploader.register({
"before-send-file": "beforeSendFile",
"before-send": "beforeSend",
"after-send-file": "afterSendFile",
}, {
beforeSendFile: function (file) {
var startTime = new Date(file.lastModifiedDate);
fileName = file.name;
var deferred = WebUploader.Deferred();
(new WebUploader.Uploader()).md5File(file, 0, 10 * 1024 * 1024)
.progress(function (percentage) {
console.log("正在读取文件");
})
.then(function (val) {
fileMd5 = val;
fm.push(fileMd5);
deferred.resolve();
});
return deferred.promise();
},
//时间点2:如果有分块上传,则每个分块上传之前调用此函数
beforeSend: function (block) {
var deferred = WebUploader.Deferred();
//上传前ajax检测一下此文件块是否已经上传
this.owner.options.formData.fileMd5 = fileMd5;
this.owner.options.formData.chunk = block.chunk;
deferred.resolve();
return deferred.promise();
},
//时间点3:所有分块上传成功后调用此函数
afterSendFile: function (file) {
var deferred = $.Deferred();
$(‘#‘ + file.id).find(‘p.state‘).text(‘执行最后一步‘);
console.log(file);
console.log(file.guid);
$.ajax({
type: "POST",
url: "/api/v1/Check/FileMerge",
data: {
guid: file.guid,
fileMd5: fm[fnum],
fileName: file.name
},
cache: false,
async: false,
success: function (response) {
fnum++;
console.log(response);
if (response.success == true) {
dataState = response;
flag = true;
} else {
flag = false;
}
deferred.resolve();
},
error: function () {
fnum++;
dataState = undefined;
flag = false;
deferred.reject();
}
});
return deferred.promise();
}
});
uploader = WebUploader.create({
resize: false,
fileNumLimit: 10,
swf: ‘/js/Uploader.swf‘,
server: ‘/api/v1/Check/FileSave‘,
pick: ‘#picker‘,
chunked: true,
chunkSize: 10 * 1024 * 1024,
chunkRetry: 5
//, formData: {
// guid: GUID
//}
});
uploader.on(‘beforeFileQueued‘, function (file) {
var isAdd = false;
for (var i = 0; i < FileExt.length; i++) {
if (file.ext == FileExt[i]) {
file.guid = WebUploader.Base.guid();
isAdd = true;
break;
}
}
return isAdd;
});
uploader.on(‘uploadBeforeSend‘, function (object, data, headers) {
//console.log(object);
headers.Authorization =Token;
data.guid = object.file.guid;
});
// 当有文件添加进来的时候
uploader.on(‘fileQueued‘, function (file) {
$list.append(‘<div id="‘ + file.id + ‘" class="item">‘ +
‘<h4 class="info">‘ + file.name + ‘</h4>‘ +
‘<input type="hidden" id="h_‘ + file.id + ‘" value="‘ + file.guid + ‘" />‘ +
‘<p class="state">等待上传...</p>‘ +
‘</div>‘);
});
// 文件上传过程中创建进度条实时显示。
uploader.on(‘uploadProgress‘, function (file, percentage) {
var $li = $(‘#‘ + file.id),
$percent = $li.find(‘.progress .progress-bar‘);
// 避免重复创建
if (!$percent.length) {
$percent = $(‘<div class="progress progress-striped active">‘ +
‘<div class="progress-bar" role="progressbar" type="button" class="btn btn-primary fright retry pbtn">重新上传</button>‘);
flag = false;
file.setStatus(‘error‘);
}
if (dataState.success == true) {
$(‘#‘ + file.id).find(‘p.state‘).text(‘已上传‘);
$(‘#‘ + file.id).find(‘button‘).remove();
} else {
$(‘#‘ + file.id).find(‘p.state‘).text(‘上传失败‘);
flag = false;
}
});
uploader.on(‘uploadError‘, function (file) {
$(‘#‘ + file.id).find(‘p.state‘).text(‘上传出错‘);
});
uploader.on(‘uploadComplete‘, function (file) {
$(‘#‘ + file.id).find(‘.progress‘).fadeOut();
});
uploader.on(‘all‘, function (type) {
if (type === ‘startUpload‘) {
state = ‘uploading‘;
} else if (type === ‘stopUpload‘) {
state = ‘paused‘;
} else if (type === ‘uploadFinished‘) {
state = ‘done‘;
}
if (state === ‘done‘) {
$btn.text(‘继续上传‘);
} else if (state === ‘uploading‘) {
$btn.text(‘暂停上传‘);
} else {
$btn.text(‘开始上传‘);
}
});
$btn.on(‘click‘, function () {
if (state === ‘uploading‘) {
uploader.stop();
} else if (state == ‘done‘) {
window.location.reload();
}
else {
uploader.upload();
}
});
});
</script>
</head>
<body>
<div id="uploader" class="wu-example">
<span class="uploader-list"></div>
<div class="btns">
<div id="picker" class="webuploader-container"><div class="webuploader-pick">选择文件</div><div name="file" class="webuploader-element-invisible" multiple="multiple"><label class="btn btn-default">开始上传</button>
</div>
</div>
</body>
</html>
后台代码:
#region 上传视频
public IActionResult Upload()
{
ViewBag.Token = HttpContext.Request.Headers["Authorization"];//获取认证信息,传递给前台,方便Ajax请求时提供
return View();
}
/// <summary>
/// 上传文件
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> FileSave()
{
var date = Request;
var files = Request.Form.Files;
long size = files.Sum(f => f.Length);
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
string fileExt = formFile.FileName.Substring(formFile.FileName.IndexOf(‘.‘)); //文件扩展名,不含“.”
long fileSize = formFile.Length; //获得文件大小,以字节为单位
//string newFileName = Guid.NewGuid().ToString() + "." + fileExt; //随机生成新的文件名
string DirPath = Path.Combine(_uploadConfig.TmpPath, Request.Form["guid"]);
if (!Directory.Exists(DirPath))
{
Directory.CreateDirectory(DirPath);
}
var filePath = DirPath + "/" + Request.Form["chunk"] + fileExt;
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
}
return Ok(new { count = files.Count, size });
}
/// <summary>
/// 合并请求
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> FileMerge()
{
bool ok = false;
string errmsg = "";
try
{
var temporary = Path.Combine(_uploadConfig.TmpPath, Request.Form["guid"]);//临时文件夹
string fileName = Request.Form["fileName"];//文件名
string fileExt = Path.GetExtension(fileName);//获取文件后缀
var files = Directory.GetFiles(temporary);//获得下面的所有文件
var finalFilePath = Path.Combine(_uploadConfig.UpLoadPath + fileName);//最终的文件名
//var fs = new FileStream(finalFilePath, FileMode.Create);
using (var fs = new FileStream(finalFilePath, FileMode.Create))
{
foreach (var part in files.OrderBy(x => x.Length).ThenBy(x => x))
{
var bytes = System.IO.File.ReadAllBytes(part);
await fs.WriteAsync(bytes, 0, bytes.Length);
bytes = null;
System.IO.File.Delete(part);//删除分块
}
Directory.Delete(temporary);//删除文件夹
ok = true;
}
}
catch (Exception ex)
{
ok = false;
errmsg = ex.Message;
log4net.Error(errmsg);
}
if (ok)
{
return Ok(new { success = true, msg = "" });
}
else
{
return Ok(new { success = false, msg = errmsg }); ;
}
}
#endregion
JS数据类型之问—概念篇
1.JS原始数据类型有哪些?引用数据类型有哪些?在 JS 中,存在着 7 种原始值,分别是:
boolean
null
undefined
number
string
symbol
bigint引用数据类型:对象Object(包含普通对象-Object,数组对象-Array,正则对象-RegExp,日期对象-Date,数学函数-Math)函数Function2.说出下面运行的结果,解释原因。
结果:
原因: 在函数传参的时候传递的是对象在堆中的内存地址值,test函数中的实参person是p1对象的内存地址,通过调用person.age = 26确实改变了p1的值,但随后person变成了另一块内存空间的地址,并且在最后将这另外一份内存空间的地址返回,赋给了p2。3.null是对象吗?为什么?结论: null不是对象。解释: 虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象然而 null 表示为全零,所以将它错误的判断为 object 。4.‘1‘.toString()为什么可以调用?其实在这个语句运行的过程中做了这样几件事情:
第一步: 创建String类实例。第二步: 调用实例方法。第三步: 执行完方法立即销毁这个实例。整个过程体现了基本包装类型的性质,而基本包装类型恰恰属于基本数据类型,包括Boolean, Number和String。
参考:《JavaScript高级程序设计(第三版)》P118
5.0.1+0.2为什么不等于0.3?0.1和0.2在转换成二进制后会无限循环,由于标准位数的限制后面多余的位数会被截掉,此时就已经出现了精度的损失,相加后因浮点数小数位的限制而截断的二进制数字在转换为十进制就会变成0.30000000000000004。
第二篇: JS数据类型之问——检测篇
1. typeof 是否能正确判断类型?对于原始类型来说,除了 null 都可以调用typeof显示正确的类型。
但对于引用数据类型,除了函数之外,都会显示"object"。
因此采用typeof判断对象数据类型是不合适的,采用instanceof会更好,instanceof的原理是基于原型链的查询,只要处于原型链中,判断永远为true
2. instanceof能否判断基本数据类型?能。比如下面这种方式:
如果你不知道Symbol,可以看看其实就是自定义instanceof行为的一种方式,这里将原有的instanceof方法重定义,换成了typeof,因此能够判断基本数据类型。
3. 能不能手动实现一下instanceof的功能?核心: 原型链的向上查找。
测试:
4. Object.is和===的区别?Object在严格等于的基础上修复了一些特殊情况下的失误,具体来说就是+0和-0,NaN和NaN。 源码如下:
第三篇: JS数据类型之问——转换篇
1. [] == ![]结果是什么?为什么?解析:== 中,左右两边都需要转换为数字然后进行比较。[]转换为数字为0。![] 首先是转换为布尔值,由于[]作为一个引用类型转换为布尔值为true,因此![]为false,进而在转换成数字,变为0。0 == 0 , 结果为true
2. JS中类型转换有哪几种?
JS中,类型转换只有三种:
转换成数字
转换成布尔值
转换成字符串
转换具体规则如下:
注意"Boolean 转字符串"这行结果指的是 true 转字符串的例子
3. == 和 ===有什么区别?
===叫做严格相等,是指:左右两边不仅值要相等,类型也要相等,例如‘1‘===1的结果是false,因为一边是string,另一边是number。
==不像===那样严格,对于一般情况,只要值相等,就返回true,但==还涉及一些类型转换,它的转换规则如下:
两边的类型是否相同,相同的话就比较值的大小,例如1==2,返回false
判断的是否是null和undefined,是的话就返回true
判断的类型是否是String和Number,是的话,把String类型转换成Number,再进行比较
判断其中一方是否是Boolean,是的话就把Boolean转换成Number,再进行比较
如果其中一方为Object,且另一方为String、Number或者Symbol,会将Object转换成字符串,再进行比较
4. 对象转原始类型是根据什么流程运行的?
对象转原始类型,会调用内置的[ToPrimitive]函数,对于该函数而言,其逻辑如下:
如果Symbol.toPrimitive()方法,优先调用再返回
调用valueOf(),如果转换为原始类型,则返回
调用toString(),如果转换为原始类型,则返回
如果都没有返回原始类型,会报错
5. 如何让if(a == 1 && a == 2)条件成立?其实就是上一个问题的应用。
第四篇: 谈谈你对闭包的理解
什么是闭包?
红宝书(p178)上对于闭包的定义:闭包是指有权访问另外一个函数作用域中的变量的函数.
MDN 对闭包的定义为:闭包是指那些能够访问自由变量的函数。 (其中自由变量,指在函数中使用的,但既不是函数参数arguments也不是函数的局部变量的变量,其实就是另外一个函数作用域中的变量。)
闭包产生的原因?首先要明白作用域链的概念,其实很简单,在ES5中只存在两种作用域————全局作用域和函数作用域,当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这就是作用域链,值得注意的是,每一个子函数都会拷贝上级的作用域,形成一个作用域的链条。 比如:
在这段代码中,f1的作用域指向有全局作用域(window)和它本身,而f2的作用域指向全局作用域(window)、f1和它本身。而且作用域是从最底层向上找,直到找到全局作用域window为止,如果全局还没有的话就会报错。就这么简单一件事情!闭包产生的本质就是,当前环境中存在指向父级作用域的引用。还是举上面的例子:
这里x会拿到父级作用域中的变量,输出2。因为在当前环境中,含有对f2的引用,f2恰恰引用了window、f1和f2的作用域。因此f2可以访问到f1的作用域的变量。那是不是只有返回函数才算是产生了闭包呢?回到闭包的本质,我们只需要让父级作用域的引用存在即可,因此我们还可以这么做:
让f1执行,给f3赋值后,等于说现在f3拥有了window、f1和f3本身这几个作用域的访问权限,还是自底向上查找,最近是在f1中找到了a,因此输出2。在这里是外面的变量f3存在着父级作用域的引用,因此产生了闭包,形式变了,本质没有改变。闭包有哪些表现形式?明白了本质之后,我们就来看看,在真实的场景中,究竟在哪些地方能体现闭包的存在?返回一个函数。刚刚已经举例。
作为函数参数传递
在定时器、事件监听、Ajax请求、跨窗口通信、Web Workers或者任何异步中,只要使用了回调函数,实际上就是在使用闭包。
以下的闭包保存的仅仅是window和当前作用域。
IIFE(立即执行函数表达式)创建闭包, 保存了全局作用域window和当前函数的作用域,因此可以全局的变量。
如何解决下面的循环输出问题?
为什么会全部输出6?如何改进,让它输出1,2,3,4,5?(方法越多越好)因为setTimeout为宏任务,由于JS中单线程eventLoop机制,在主线程同步任务执行完后才去执行宏任务,因此循环结束后setTimeout中的回调才依次执行,但输出i的时候当前作用域没有,往上一级再找,发现了i,此时循环已经结束,i变成了6。因此会全部输出6。解决方法:1、利用IIFE(立即执行函数表达式)当每次for循环时,把此时的i变量传递到定时器中
2、给定时器传入第三个参数, 作为timer函数的第一个函数参数
let使JS发生革命性的变化,让JS有函数作用域变为了块级作用域,用let后作用域链不复存在。代码的作用域以块级为单位,以上面代码为例:
因此能输出正确的结果。
第五篇: 谈谈你对原型链的理解
1.原型对象和构造函数有何关系?在JavaScript中,每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象。当函数经过new调用时,这个函数就成为了构造函数,返回一个全新的实例对象,这个实例对象有一个proto属性,指向构造函数的原型对象。
2.能不能描述一下原型链?JavaScript对象通过prototype指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条, 即原型链。
对象的 hasOwnProperty() 来检查对象自身中是否含有该属性使用 in 检查对象中是否含有某个属性时,如果对象中没有但是原型链中有,也会返回 true
第六篇: JS如何实现继承?
第一种: 借助call
这样写的时候子类虽然能够拿到父类的属性值,但是问题是父类原型对象中一旦存在方法那么子类无法继承。那么引出下面的方法。
看似没有问题,父类的方法和属性都能够访问,但实际上有一个潜在的不足。举个例子:
可以看到控制台:
明明我只改变了s1的play属性,为什么s2也跟着变了呢?很简单,因为两个实例使用的是同一个原型对象。那么还有更好的方式么?第三种:将前两种组合
可以看到控制台:
之前的问题都得以解决。但是这里又徒增了一个新问题,那就是Parent3的构造函数会多执行了一次(Child3.prototype = new Parent3();)。这是我们不愿看到的。那么如何解决这个问题?第四种: 组合继承的优化1
这里让将父类原型对象直接给到子类,父类构造函数只执行一次,而且父类属性和方法均能访问,但是我们来测试一下:
子类实例的构造函数是Parent4,显然这是不对的,应该是Child4。
第五种(最推荐使用): 组合继承的优化1
这是最推荐的一种方式,接近完美的继承,它的名字也叫做寄生组合继承。
ES6的extends被编译后的JavaScript代码ES6的代码最后都是要在浏览器上能够跑起来的,这中间就利用了babel这个编译工具,将ES6的代码编译成ES5让一些不支持新语法的浏览器也能运行。那最后编译成了什么样子呢?
核心是_inherits函数,可以看到它采用的依然也是第五种方式————寄生组合继承方式,同时证明了这种方式的成功。不过这里加了一个Object.setPrototypeOf(subClass, superClass),这是用来干啥的呢?答案是用来继承父类的静态方法。这也是原来的继承方式疏忽掉的地方。
追问: 面向对象的设计一定是好的设计吗?
不一定。从继承的角度说,这一设计是存在巨大隐患的。从设计思想上谈谈继承本身的问题假如现在有不同品牌的车,每辆车都有drive、music、addOil这三个方法。
现在可以实现车的功能,并且以此去扩展不同的车。但是问题来了,新能源汽车也是车,但是它并不需要addOil(加油)。如果让新能源汽车的类继承Car的话,也是有问题的,俗称"大猩猩和香蕉"的问题。大猩猩手里有香蕉,但是我现在明明只需要香蕉,却拿到了一只大猩猩。也就是说加油这个方法,我现在是不需要的,但是由于继承的原因,也给到子类了。
继承的最大问题在于:无法决定继承哪些属性,所有属性都得继承。
当然你可能会说,可以再创建一个父类啊,把加油的方法给去掉,但是这也是有问题的,一方面父类是无法描述所有子类的细节情况的,为了不同的子类特性去增加不同的父类,代码势必会大量重复,另一方面一旦子类有所变动,父类也要进行相应的更新,代码的耦合性太高,维护性不好。那如何来解决继承的诸多问题呢?用组合,这也是当今编程语法发展的趋势,比如golang完全采用的是面向组合的设计方式。顾名思义,面向组合就是先设计一系列零件,然后将这些零件进行拼装,来形成不同的实例或者类。
代码干净,复用性也很好。这就是面向组合的设计方式。
构建虚拟主机
■虚拟Web主机●在同一台服务器中运行多个Web站点,其中每一一个站点并不独立占用一台真正的计算机■httpd支持的虚拟主机类型●基于域名的虚拟主机●基于IP地址的虚拟主机●基于端口的虚拟主机:
构建虚拟主机一 基于域名
■应用示例:
●构建2个虚拟Web站点www.kgc.com, IP地址为173.17.17.11 www.kcce.com, IP地址为173.17.17.11●在浏览器中访问这两个域名时,分别显示不同的内容
1.为虚拟主机提供域名解析
[root@dnssvr ~]# vim /var/named/chroot/var/named/kgc.com.zone@ IN NS dnssvr.kgc.com.dnssvr IN A 173.1 7.1 7.2www IN A 173.17.17.11[root@dnssvr ~]# vim /var/named/chroot/var/named/kcce.com.zone......@ IN NS dnssvr.kgc.com.www IN A 173.17.1711
2.为虚拟主机准备网页文档
[root@www ~]# mkdir -p /var/www/html/kgc[root@www ~]# mkdir -p /var/www/html/accp[root@www ~]# echo " <h1>www.kgc.com</h1>" >/var/www/html/kgccom/index.html[root@www ~]# echo! <h1>www.accp.com</h1>" >/var/www/html/kccecom/index.html
3.添加虚拟主机配置
[root@www ~]# vim /usr/local/httpd/conf/extra/httpd-vhosts.comf<VirtualHost :80>DocumentRoot "/var/www/html/kgccom"ServerName www.kgc.comErrorLog "logs/www.kgc.com.error_log"CustomLog "logs/www.kgc.com.access_log" common< Directory "/var/www/html" >Require all granted</Directory></VirtualHost><VirtualHost :80>DocumentRoot "/var/www/html、accpServerName www.accp.comErrorLog "logs/www.accp.com.error_log"CustomLog "logs/www.accp.com.access_log" common< Directory "/var/www/html" >Require all granted</Directory>< /VirtualHost>
4.在客户机分别访问虚拟Web主机
构建虚拟主机——基于端口
■应用示例:
●构建2个虚拟Web站点
www.kgc.com, IP地址、 端口为173.17.17.11:80www.accp.com, IP地址、 端口为173.17.17.11:8353在浏览器中访问这两个端口时,分别显示不同的内容[root@www ~]# vim /usr/loca/httpd/conf/extra/httpd-vhosts.conf< VirtualHost 173.17.17.11:80习DocumentRoot "/var/wwwhtml/kccecom"ServerName www.kgc.com</VirtualHost><VirtualHost 173.17.17.11:8353)DocumentRoot "/var/www/html/kccepad"ServerName www.accp.com< NirtualHost>Listen 80Listen 8353
构建虚拟主机一 基于IP
■应用示例:●构建2个虚拟Web站点www.kgc.com, IP地址为220.181.120.61www.kcce.com, IP地址为122.115.32.133在浏览器中访问这两个IP时P分别显示不同的内容[root@www ~]# vim /usr/local/httpd/conf/extra/httpd-vhosts.conf< VirtualHost 220.181.120.61:80>DocumentRoot "/var/www/html/kgccom"ServerName www.kgc.com</NirtualHost> I<VirtualHost 122.115.32.133:80>DocumentRoot "/var/www/htm/kccecom"ServerName www.kcce.com</VirtualHost>
总结:Apache/Tomcat/JBOSS/Nginx区别 .
1、Apache是Web服务器,Tomcat是应用(Java)服务器。Tomcat在中小型系统和并发访问用户不是很多的场合下被普遍使用。Apache支持静态页,Tomcat支持动态的。
2、Jetty:Tomcat内核作为其Servlet容器引擎,并加以审核和调优.大中型系统可以应用。能够提供数据库连接池服务,还支持其他 Web 技术的集成,譬如PHP、.NET 两大阵营.
3、JBoss是一个管理EJB的容器和服务器,但JBoss核心服务不包括支持servlet/JSP的WEB容器,一般与Tomcat或Jetty绑定使用。
4、Nginx是目前性能最高的HTTP服务器。其特点是占有内存少,并发能力强。Nginx代码完全用C语言从头写成。
所以:Apache--Nginx;Tomcat--Jetty;JBoss。这三组可以组合了。
具体描述:
一、Apache+Tomcat
Apache支持静态页,Tomcat支持动态的,比如Servlet等,
一般使用Apache+Tomcat的话,Apache只是作为一个转发,对JSP的处理是由Tomcat来处理的。
Apche可以支持PHPcgiperl,但是要使用Java的话,你需要Tomcat在Apache后台支撑,将Java请求由Apache转发给Tomcat处理。
Apache是Web服务器,Tomcat是应用(Java)服务器,它只是一个Servlet(JSP也翻译成Servlet)容器,可以认为是Apache的扩展,但是可以独立于Apache运行。
这两个有以下几点可以比较的:
◆两者都是Apache组织开发的
◆两者都有HTTP服务的功能
◆两者都是免费的
不同点:
Apache是专门用了提供HTTP服务的,以及相关配置的(例如虚拟主机、URL转发等等)
Tomcat是符合Java EE的JSP、Servlet标准下开发的一个JSP服务器:Tomcat在中小型系统和并发访问用户不是很多的场合下被普遍使用。
二、Jetty
而Jetty采用业界最优的开源Java Web引擎,将Java社区中下载量最大,用户数最多,标准支持最完备的Tomcat内核作为其Servlet容器引擎,并加以审核和调优。单纯的Tomcat性能有限,在很多地方表现有欠缺,如活动连接支持、静态内容、大文件和HTTPS等。除了性能问题,Tomcat的另一大缺点是它是一个受限的集成平台,仅能运行Java应用程序。企业在使用时Tomcat,往往还需同时部署Apache WebServer以与之整合。此配置较为繁琐,且不能保证性能的优越性。
Jetty通过使用APR和Tomcat本地技术的混合模型来解决Tomcat的诸多不足。混合技术模型从最新的操作系统技术里提供了最好的线程和事件处理。结果,Jetty达到了可扩展性,性能参数匹配甚至超越了本地Apache HTTP服务器或者IIS。譬如Jetty能够提供数据库连接池服务,不仅支持 JSP 等 Java 技术,同时还支持其他 Web 技术的集成,譬如PHP、.NET 两大阵营。
三、Nginx
Nginx具有很高的稳定性。其它HTTP服务器,当遇到访问的峰值,或者有人恶意发起慢速连接时,也很可能会导致服务器物理内存耗尽频繁交换,失去响应,只能重启服务器。例如当前apache一旦上到200个以上进程,web响应速度就明显非常缓慢了。而Nginx采取了分阶段资源分配技术,使得它的CPU与内存占用率非常低。nginx官方表示保持10,000个没有活动的连接,它只占2.5M内存,所以类似DOS这样的攻击对nginx来说基本上是毫无用处的。就稳定性而言,nginx比lighthttpd更胜一筹。
四、JBoss
JBoss一个基于J2EE的开放源代码的应用服务器.JBoss是一个管理EJB的容器和服务器,但JBoss核心服务不包括支持servlet/JSP的WEB容器,一般与Tomcat或Jetty绑定使用。
JBoss与Web服务器在同一个Java虚拟机中运行 JBoss运行后后台管理界面,Servlet调用EJB不经过网络,从而大大提高运行效率,提升安全性能。
事件的定义
完整的 key press 过程分为两个部分:1. 按键被按下;2. 按键被松开。
当按钮被按下时,发生 keydown 事件。
keydown() 方法触发 keydown 事件,或规定当发生 keydown 事件时运行的函数。
当按钮被松开时,发生 keyup 事件。它发生在当前获得焦点的元素上。
keyup() 方法触发 keyup 事件,或规定当发生 keyup 事件时运行的函数。
keypress 事件与 keydown 事件类似。当按钮被按下时,会发生该事件。它发生在当前获得焦点的元素上。
不过,与 keydown 事件不同,每插入一个字符,就会发生 keypress 事件。
keypress() 方法触发 keypress 事件,或规定当发生 keypress 事件时运行的函数。
使用情况:
(which事件)
$("input").keydown(function(event){
$("div").html("Key: " + event.which);
});
which 属性指示按了哪个键或按钮。(和进行了标准化。)
1.理论上它可以绑定到任何元素,但keydown/keyup事件只是发送到具有焦点的元素上,不同的浏览器中,可获得焦点的元素略有不同,但是表单元素总是能获取焦点,所以对于此事件类型表单元素是最合适的
2.在input元素上绑定keydown事件时获取的内容都是之前输入的,当前输入的获取不到
3.keydown()事件触发在文字还没有敲进文本框,这时如果在kevdown事件中输入文本框中的文本,得到的是触发键盘事件前的文本
4.keyup()事件触发时整个键盘事件的操作已经完成,获得的是触发键盘事件后的文本
5.keypress()捕获浏览器键盘输入的时候
6.keypress事件与keydown和keyup的主要区别(所以使用组合键时需要使用keydown事件兼容)
只能捕获单个字符,不能捕获组合键
无法响应系统功能键(如delete,backspace)
不区分小键盘和主键盘的数字字符
7.KeyPress主要用来接收字母、数字等ANSI字符
8.KeyDown 和 KeyUP 事件过程可以处理任何不被 KeyPress 识别的击键,如:功能键(F1-F12)、编辑键、定位键以及任何这些键和键盘换档键的组合等
20岁老牌网页程序语言PHP,PHP 7新版,这是十年来的首次大改版,最大特色是在性能上的大突破,能比前一版PHP 5快上一倍,PHP之父Rasmus Lerdorf表示,甚至能比HHVM虚拟机下的PHP程序性能更快。
HHVM 是脸书为自家网站特性而量身客制的php优化机制,不见得适用任何网站。但Rasmus Lerdorf表示,新版目标之一就是要让任何网站开发者,就连使用开发框架Drupal、开源电子商务系统Opencart时,都能有不输使用HHVM 技术的性能。在新版发表前夕,他也趁来台参加PHPConf Taiwan年会时,分享PHP 7性能大突破的关键。
一个20年来历经了多 次改版和无数次优化的成熟语言,还能有性能提高一倍的突破绝非易事,Rasmus Lerdorf坦言,不像一般新项目多半容易找出许多改进空间,新版PHP并非修改部分程序就达到了如此的成果。反而是,透过大量细节优化和性能累加 后,PHP 7才具备了不输HHVM的执行性能。
除了从减少内存的使用着手外,Rasmus Lerdorf更检视CPU的Cache line的运作原理,了解程序代码如何与CPU互动、编译程序如何在新CPU架构下编译程序代码等细节,确保PHP 7的程序代码符合现代CPU的架构。虽然每个项目的优化对性能贡献都低于0.5%,但由于优化的项目很多,或是某项改善的功能会被应用程序反复呼叫,整体 修正的综效结果就能有如此大的进展。
受HHVM刺激,决定打造兼具性能与功能的PHP
Facebook为了优化PHP运作,搭配JIT编译而打造出虚拟机HHVM。而HHVM虽然拥有快速的执行性能,其为特定用途优化的设计,只能满足小部分的开发者。反之,Rasmus Lerdorf除了想提升PHP的性能表现外,也想要同时满足高端使用者以及业余使用者的需求,让PHP 7成为兼备性能表现及通用功能的程序语言。
不使用外挂框架的PHP的运算性能表现都很优异,但是受到外加框架的影响,原本可以在数秒内处理上千个网页要求的 PHP,性能大幅下降,变为只能处理数十个要求。Rasmus Lerdorf表示,在HHVM出现之前,相较于对PHP性能表现的要求,使用者比较在意PHP能否降低网页开发的难度,而这些框架能让开发者的工作变得 比较简单。但是在Facebook推出HHVM后,引出许多重视PHP性能表现的使用者,让Rasmus Lerdorf意识到许多使用者有性能表现的需求。他开始思考如何将HHVM的JIT架构与PHP融合。
Rasmus Lerdorf表示,他不能放弃PHP的主要架构,虽然他们曾经考虑过融合两者,但是,HHVM在使用上有很多的限制。虽然HHVM对Facebook及 许多开发者是非常好的工具,但对于PHP项目来说,HHVM的使用范畴还不够宽广,只能符合Facebook或是Wikipedia等特定项目的需求。
非强型别语言的PHP,导入JIT是难上加难
然而,在PHP中加入JIT编译是件非常困难的事情。Rasmus Lerdorf表示,JIT必须学会辨认程序的运作模(Patterns),例如了解哪些部份为重要的程序代码,并且在程序运作前,预测程序被呼叫的时 机,或是哪些部分的程序会呼叫。
而HHVM为了在使用JIT编译,某种程度上受限了PHP的发展。HHVM的用户 必须清楚宣告变量的性质,但是使用PHP的开发者,可以先宣告没有性质的类别(Class),后续再指定类别的变量属性。「在没有任何限制下,将JIT加 入PHP是我们要做的事。」他表示,PHP必须顾及Wordpress、Drupal等框架的开发者,不能任意停止对此些框架的支持。故与HHVM相 比,PHP在打造JIT的条件限制更多。
目前,PHP核心贡献者之一的Dmitry Stogov开发一个原型JIT,并且使用某些实验性的应用程序去测试运作。Rasmus Lerdorf表示,如果将此JIT用于执行某些重复性的运算或是循环程序,得以让PHP 7性能又再快上10倍。
链接: 提取码:x2p5
免费分享,但是X度限制严重,如若链接失效点击链接或搜索加群 群号。
是一个面向.NET开发人员的开源、跨平台的机器学习框架。
使用,您可以轻松地为诸如、、、、等场景构建自定义机器学习模型。
从0.8版开始,支持评估特性的重要性,从而了解哪些列对于预测最终值更重要。
排列特征的重要性在于,突出最重要的特征,以便理解哪些特征必须包括,哪些不用包括;从数据集中排除一些特性意味着减少噪音,结果会更好。
因此,通过PFI,我们可以了解在我们的学习pipeline中什么是最重要的列,并使用它们来预测值。
Pipeline
第一步与预测值的步骤相同,因此必须构建pipeline。
例如,一个标准pipeline可以是这样的:
var mlContext = new MLContext();
var dataView = MlContext.Data.LoadFromTextFile<T>(dataPath, separator, hasHeader: false);
var pipeline = MlContext.Transforms.CopyColumns("Label", _predictedColumn.ColumnName).Append(MlContext.Transforms.Concatenate(_featureColumn, _concatenatedColumns));
这是一个非常简单的pipeline,从文件中加载数据,复制label列并添加feature列。
现在pipeline已经配置好了,我们可以构建模型了。
Model
建立模型意味着获取pipeline、附加选择算法,对其进行拟合和变换。
var tranformedDataView = pipeline.Append(MlContext.Regression.Trainers.LbfgsPoissonRegression()).Fit(DataView).Transform(DataView);
结果是一个转换后的数据视图,其中应用了pipeline转换所有数据,我们将在Permutation Feature Importance方法中使用这些转换。
Metrics
为了获得PFI指标,除了转换后的数据视图,我们还需要一个转换器:
var transformer = pipeline.MlContext.Regression.Trainers.LbfgsPoissonRegression().Fit(tranformedDataView);
现在我们可以得到度量:
var permutationMetrics = pipeline.MlContext.Regression.PermutationFeatureImportance(transformer, transformedDataView, permutationCount: 3);
使用permutation count参数,我们可以指定希望为回归度量执行的观察次数。
结果是一个回归度量统计数据的数组,并在一个特定的度量上可用的排序,比如平均值:
var regressionMetrics = permutationMetrics.Select((metric, index) => new { index, metric.RSquared }).OrderByDescending(features => Math.Abs(features.RSquared.Mean));
有了循环,我们现在可以打印的指标:
foreach (var metric in regressionMetrics)
{
if (metric.index >= transformedData.Schema.Count || (transformedData.Schema[metric.index].IsHidden || transformedData.Schema[metric.index].Name == "Label" || transformedData.Schema[metric.index].Name == "Features"))
continue;
Console.WriteLine($"{transformedData.Schema[metric.index].Name,-20}| {metric.RSquared.Mean:F6}");
}
在这个示例的情况下,输出是:
有了这个统计数据,我们可以了解什么是最重要的特性,并将更改应用到pipeline构建中。
这篇文章的源代码可以在上找到。
Shell脚本如下:
vim monitor.sh
#!/bin/bash
while true # 无限循环
flag=`ps -aux |grep "httpd" |grep -v "grep" |wc -l`
do
if [[ $flag -eq 0 ]] # 判断进程数如果等于0,则启动httpd
then
`systemctl start httpd` # 启动httpd
echo `date` - "Apache restart" >> running.log # 将重启时间写入自定义的日志文件
else
echo "Apache is running..." >> /dev/null
fi
sleep 3s # 延迟3秒后进入下次循环
done
运行脚本:bash monitor.sh &
命令末尾的 & 号,意思是将这个任务放到后台去执行
那么如何停止脚本运行呢?
(1)首先查找运行脚本的进程PID号
ps -aux |grep "bash monitor.sh"
(2)终止脚本进程:
kill -9 进程PID号
对脚本做一些说明:
ps -aux | grep # 查找进程
参数:-aux 意思是显示所有包含其他使用者的进程
ps -aux | grep "process_name"
若只执行这条命令,会导致出现一个 grep 进程,也就是说若只用上面的命令,会永远得到至少一个进程(grep进程),所以还需要用下面的命令,排除 grep 本身这个进程
grep -v "grep"
最后再用 wc -l 命令统计进程数
if 判断如果获得的进程数等于0,则证明服务没有运行,执行启动命令
sleep命令可以用来将目前动作延迟一段时间
sleep 1 延迟1秒
sleep 1s 延迟1秒
sleep 1m 延迟1分钟
sleep 1h 延迟1小时
sleep 1d 延迟1天
如何给zencart网站提速
这里提供一些建议,前提是您用的是最新版的Zen Cart:
1. 商店设置-Sessions-IP到主机名转换,设置为False
2. 关闭分类计数:
a. 管理页面->商店设置->基本设置->显示分类计数 = false
b. 管理页面->商店设置->基本设置->显示分类计数-管理员 = false
(在管理页面编辑分类)
3. 打开GZip压缩
管理页面->商店设置->GZip压缩->打开GZip压缩 = 1
4. 关闭厂家方框
管理页面->工具->外观控制
- 找到 sideboxes/manufacturers.php 条目,设置为 "OFF"
5. 确认进行了图像优化... 尤其是缩略图要用小图像,商品页面用中图像,("_MED"),点击放大时的图像为大图像("_LRG")。
.NET实时2D渲染入门·动态时钟
从小以来“坦克大战”、“魂斗罗”等游戏总令我魂牵梦绕。这些游戏的基础就是实时渲染,以前没意识,直到后来找到了。我的实时渲染入门,是从这个开始的。
本文将使用我写的“准游戏引擎”完成。它是对和库浅层次的封装,隐藏了一些细节,简化了一些调用。同时还保留了的原汁原味。
本文的最终效果如下:
绘制动态时钟
要绘制动态时钟,需要有以下步骤:
创建一个实时渲染窗口;
画一个圆圈,表示时钟边缘;
在圆圈内等距离画上个分钟刻度,其中个比较长,为小时刻度;
用不同粗细、不同长短、不同颜色的画笔画上时钟、分钟和秒钟。
实时渲染窗口
其中中的表示垂直同步,玩过游戏的可能见过,这个设置可以在尽可能节省资源的同时得到最佳的呈现效果。
熟悉的肯定知道,这种写法和非常像,执行效果如下:
注意:
其实继承于,确实是基于“”,但实质却和“拖控件”完全不一样。“控件”是模态的,本身有状态,但是实时渲染,界面完全没有状态,需要动态每隔一个垂直同步时间(如秒)全部清除,然后再重绘一次。
画圆圈
简单封装了,可以直接使用里面的属性来访问相关资源,包括:
动画库
动画管理器
除此之外,还进一步封装了以下组件,以简化图片、文字、颜色等调用和渲染:
简化创建
简化加载图片
简化创建
方法,简化使用颜色
这里我们将使用,这在中的名字叫。
回到事件,它包含两个参数::
其中就是原窗口,可以用外层的代替;
参数就是绘图的核心,我们将围绕它进行绘制。
要画圆圈,得先算出一个能放下一个完整圆的半径,并留下少许空间():
然后调用参数,使用黑色画笔将圆画出来,线宽为半径:
执行效果如下:
可见圆只显示了四分之一,要显示完整的圆,必须将其“移动”到屏幕正中心,我们可以调整圆的参数,将中心点从改成,或者用更简单的办法,通过矩阵变换:
注意:“矩阵变换”这几个字听起来总令人联想到“高数”,挺吓人的。但实际是并不是非要知道线性代码基础才能使用。首先只要知道它能完成任务即可,之后再慢慢理解也行。
有多种方法可以完成像平移这样的任务,但通常来说使用“矩阵变换”更简单,更不伤脑筋,尤其是多个对象,进行旋转、扭曲等复杂、或者组合操作等,这些操作如果不使用“矩阵变换”会非常非常麻烦。
这样,即可将该圆“平移”至屏幕正中心,执行效果如下:
方法完整代码:
画刻度
刻度就是线条,共个分钟刻度和个时钟刻度,其中分钟刻度较短,时钟刻度较长。
刻度的一端是沿着圆的边缘,另一端朝着圆的中心,边缘位置可以通过等三角函数计算出来……呃,可能早忘记了,不怕,我们有“矩阵变换”。
利用矩阵变换,可以非常容易地完成这项工作:
执行效果如下:
注意:此处用到了矩阵乘法:
注意乘法是有顺序的,这符合空间逻辑,可以这样想想,先旋转再平移,和先平移再旋转显然是有区别的。
然后再加上长时钟,只需在原代码基础上加个判断即可,如果,则为长时钟,粗细设置为:
执行效果如下:
画时、分、秒钟
时、分、秒钟是动态的,必须随着时间变化而变化;其中时钟最短、最粗,分钟次之,秒钟最细长,然后时钟必须叠在分钟和秒钟之上。
用代码实现,可以先画秒钟、再画分钟和时钟,即可实现重叠效果。还可以通过设置一定的透明度和不同的颜色,可以让它们区分更明显。
获取当前时间可以通过来完成,提供了时、分、秒和毫秒,可以轻松地计算各个指针应该指向的位置。
画秒钟的代码如下,显示为蓝色,长度为倍半径,宽度为半径:
效果如下:
依法炮制,可以画出分钟和时钟:
效果如下:
优化
其实到了这一步,已经是一个完整的,可运行的时钟了,但还能再优化优化。
半透明时钟
首先可以设置一定的半透明度,使三根钟重叠时不显得很突兀,代码如下:
只需将原本的等颜色改成自定义,并且指定参数为(表示半透明)即可,效果如下:
时钟两端的尖角或者圆角
可以很方便地控制绘制的线段两端,有许多风格可供选择,具体可以参见枚举:
此处我们将使用用于做中心点,用用于做针尖,首先创建一个对象:
然后在调用时,将参数传入最后一个参数即可:
执行效果如下(可见有那么点意思了):
平滑移动
是实时渲染,我们不能浪费这实时二字带来的好处。更何况显示出来的时钟也不太合理,因为当时时间是,此时时钟应该指向偏的位置。但现在由于忽略了这一分量,指向的是,这不符合实时的时钟。
因此计算小时角度时,可以加入分钟分量,计算分钟角度时,可以加入秒钟分量,计算秒钟角度时,也可以加入毫秒的分量。代码只需将矩阵变换代码稍微变动一点点即可:
执行效果如下:
阴影效果
和边缘刻度不一样,时钟多少是和窗口底层有距离的,因此怎么说也会显示一些阴影效果。这在中也能轻易实现。代码会复杂一点,过程如下:
先将创建一个临时的;
将时、分、秒钟绘制到这个中;
创建一个,传入这个的内容生成一个阴影贴图;
调用将先绘制;
调用绘制最后真正的时、分、秒钟。
注意这个过程的顺序不能错,否则可能出现阴影显示的真实物体上的虚幻效果。
临时的和可以在和事件中创建和销毁:
其中从进行获取。
先将的属性指定这个,但又同时保存老的属性用于稍后绘制:
注意是有必要的,否则将出现重影:
这样即可将时钟单独绘制到中,对这个生成一个阴影:
最后进行绘制,绘制时记得顺序:
注意两点:
首先,设置是有必要的,否则会上文的矩阵变换会一直保持作用;
然后两次设置也是有必要的,因为此时的绘制相当于是图片,按照默认的高显示会导致显示模糊,因此显示图片时需要改成点对点显示;
效果如下:
这个阴影默认是完全重叠的,现实中这种光线较小,加一点点平移效果可能会更好:
效果如下(显然更逼真了):
更好的动画
有些时钟的秒确实是这样动的,但我印象中儿时的记忆,秒是一格一格地动,它是每动一下,停顿一下再动的那种感觉。
为了实现这种感觉,我加入了的功能,这也是组件的一部分,我的中稍微封装了一下。使用时需要引入一个进行配合:
注意此处我使用了事件,这也是中封装的,可以在绘制呈现前执行一段更新逻辑的代码。
然后后面的绘制时,将获取秒的矩阵变换参数改为变量即可:
最后的执行效果如下:
看起来一切正常,但……如果经过分钟满时,会出现这种情况:
这是因为秒数从秒到秒的动画,是一个递减的过程(),因此秒钟反向转了一圈,这明显不对。
解决这个问题可以这样考虑,如果当前是秒,我们假装它是秒即可,这时计算角度不会出错,矩阵变换也没任何问题,通过强大的功能,可以不需要额外语句,在表达式内即可解决:
最后的最后,最终效果如下:
结语
说来这是我和我老婆的爱情故事。
记得6年前我老婆第一次来我出租房玩,然后……我给她感受了作为一个程序员的“浪漫”,花了一整个下午时间,把这个从开始做了出来给她看,不过那时我还在用。多年后和她说起这个入门,她仍记忆尤新。
本文中最终效果的代码,可以从我的仓库下载:
有了,那些代码已经远比当年简单,我的确是从这个例子出发,做出了许多好玩的东西,以后有机会我会慢慢介绍,敬请期待。
喜欢的朋友 请关注我的微信公众号:【DotNet骚操作】
SpringMVC默认使用MappingJacksonHttpMessageConverter对json数据进行转换
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
使用@ResponseBody注解实现将Controller方法返回对象转换json响应给客户端。
文件上传
第一种方式:使用commons-fileupload组件实现文件上传,需要用到commons-fileupload和commons-io
第二种方式:
1、进入控制面板,选择大图标显示,选择“程序与功能”;
2、进入“程序和功能”,选择“启用和关闭Windows功能”;
3、点击“启用或关闭Windows功能”,进入Windows功能界面,找到Internet Information Services,设置相关项如下:
4、点击确定,等待安装完成;
5、安装完成后,重新打开控制面板,找到“管理工具”;
6、进入“管理工具”,找到“Internet Information Services (IIS)管理器”;
7、双击“Internet Information Services (IIS)管理器”,进入IIS管理界面;
8、展开左侧的站点树,右击“网站”选择“添加网站”;
9、网站名称任取,物理路径选择你想要的展示的网页的目录,IP地址选择本机IP,端口默认80,不作修改,确认无误后,点击确定;
10、接下来在浏览器地址栏输入所建网站的IP地址,结果正常显示如下,表名Web服务器搭建成功。
1)、实时应用:如在线聊天,实时通知推送等等(如socket.io)
2)、分布式应用:通过高效的并行I/O使用已有的数据
3)、工具类应用:海量的工具,小到前端压缩部署(如grunt),大到桌面图形界面应用程序
4)、游戏类应用:游戏领域对实时和并发有很高的要求(如网易的pomelo框架)
5)、利用稳定接口提升Web渲染能力
6)、前后端编程语言环境统一:前端开发人员可以非常快速地切入到服务器端的开发(如著名的纯Javascript全栈式MEAN架构)
定位
定位有三种:
1.相对定位 2.绝对定位 3.固定定位
这三种定位,每一种都暗藏玄机,所以我们要一一单讲。
相对定位
相对定位:相对于自己原来的位置定位
现象和使用:
1.如果对当前元素仅仅设置了相对定位,那么与标准流的盒子什么区别。
2.设置相对定位之后,我们才可以使用四个方向的属性: top、bottom、left、right
特性:
1.不脱标
2.形影分离
3.老家留坑(占着茅房不拉屎,恶心人)
所以说相对定位 在页面中没有什么太大的作用。影响我们页面的布局。我们不要使用相对定位来做压盖效果
用途:
1.微调元素位置
2.做绝对定位的参考(父相子绝)绝对定位会说到此内容。
参考点:
自己原来的位置做参考点。
绝对定位
特性:
1.脱标 2.做遮盖效果,提成了层级。设置绝对定位之后,不区分行内元素和块级元素,都能设置宽高。
参考点(重点):
一、单独一个绝对定位的盒子
1.当我使用top属性描述的时候 是以页面的左上角(跟浏览器的左上角区分)为参考点来调整位置
2.当我使用bottom属性描述的时候。是以首屏页面左下角为参考点来调整位置。
二、以父辈盒子作为参考点
1.父辈元素设置相对定位,子元素设置绝对定位,那么会以父辈元素左上角为参考点,这个父辈元素不一定是爸爸,它也可以是爷爷,曾爷爷。
2.如果父亲设置了定位,那么以父亲为参考点。那么如果父亲没有设置定位,那么以父辈元素设置定位的为参考点
3.不仅仅是父相子绝,父绝子绝 ,父固子绝,都是以父辈元素为参考点
注意了:父绝子绝,没有实战意义,做站的时候不会出现父绝子绝。因为绝对定位脱离标准流,影响页面的布局。相反‘父相子绝’在我们页面布局中,是常用的布局方案。因为父亲设置相对定位,不脱离标准流,子元素设置绝对定位,仅仅的是在当前父辈元素内调整该元素的位置。
还要注意,绝对定位的盒子无视父辈的padding
作用:页面布局常见的“父相子绝”,一定要会!!!!
绝对定位的盒子居中
当做公式记下来吧!
*{
padding: 0;
margin: 0;
}
.box{
width: 100%;
height: 69px;
background: #000;
}
.box .c{
width: 960px;
height: 69px;
background-color: pink;
/*margin: 0 auto;*/
position: relative;
left: 50%;
margin-left: -480px;
/*设置绝对定位之后,margin:0 auto;不起任何作用,如果想让绝对定位的盒子居中。当做公式记下来 设置子元素绝对定位,然后left:50%; margin-left等于元素宽度的一半,实现绝对定位盒子居中*/
}
固定定位
固定当前的元素不会随着页面滚动而滚动
特性:
1.脱标 2.遮盖,提升层级 3.固定不变
参考点:
设置固定定位,用top描述。那么是以浏览器的左上角为参考点
如果用bottom描述,那么是以浏览器的左下角为参考点
作用: 1.返回顶部栏 2.固定导航栏 3.小广告
z-index
这个东西非常简单,它有四大特性,每个特性你记住了,页面布局就不会出现找不到盒子的情况。
z-index 值表示谁压着谁,数值大的压盖住数值小的,
只有定位了的元素,才能有z-index,也就是说,不管相对定位,绝对定位,固定定位,都可以使用z-index,而浮动元素不能使用z-index
z-index值没有单位,就是一个正整数,默认的z-index值为0如果大家都没有z-index值,或者z-index值一样,那么谁写在HTML后面,谁在上面压着别人,定位了元素,永远压住没有定位的元素。
从父现象:父亲怂了,儿子再牛逼也没用
JQuery入门篇
jQuery选择器
“$”表示JQuery对象
根据ID查找
$(‘#var’)表示将一个id值为var的DOM节点封装成一个jQuery对象,DOM节点必须以“#”开头。
例如:有<div id="var "></div>存在,使用$(‘#var’)之后会将其封装成[<div id="var "></div>],但是如果不存在id=”var”,返回的jQuery对象是[],而不是一个null或undefined。
// 查找<div id="abc">:
var div = $(‘#abc‘); //获取id=”abc”的节点
根据class查找
1、只存在一个class样式:
var a = $(‘.red‘); // 所有节点包含`class="red"`都将返回
// 例如:
// <div class="red">...</div>
// <p class="green red">...</p>
根据Tag(标签)查找
var ps = $(‘p‘); // 返回所有<p>节点
ps.length; // 数一数页面有多少个<p>节点
根据属性查找
var email = $(‘[name=email]‘); // 找出<??? name="email">
var passwordInput = $(‘[type=password]‘); // 找出<??? type="password">
//当属性值包含空格等特殊字符时,需要加上“”
var a = $(‘[items="A B"]‘); // 找出<??? items="A B">
使用前缀、后缀的属性查找
var icons = $(‘[name^=icon]‘); // 找出所有name属性值以icon开头的DOM
// 例如: name="icon-1", name="icon-2"
var names = $(‘[name$=with]‘); // 找出所有name属性值以with结尾的DOM
// 例如: name="startswith", name="endswith"
组合查找
var emailInput = $(‘input[name=email]‘); // 只查找input标签里name属性为Email的DOM节点,不会找出<div name="email">
多项选择器查找
$(‘p,div‘); // 把<p>和<div>都选出来
$(‘p.red,p.green‘); // 把<p class="red">和<p class="green">都选出来
要注意的是,选出来的元素是按照它们在HTML中出现的顺序排列的,而且不会有重复元素。例如,不会被上面的选择两次。
层级选择器
对于两个DOM元素具有层级关系,可以使用层级选择器。层级选择器之间使用空格。
$(‘form[name=upload] input’); //选择form表单中name属性为upload的表单内容
$(‘form.test p input‘); // 在form表单选择被<p>包含的<input>的节点
子选择器
子选择器跟层级选择器很类似,区别在于子选择器限定的层级关系必须是父子节点。
<!-- HTML结构 -->
<div class="testing">
<ul class="lang">
<li class="lang-javascript">JavaScript</li>
<li class="lang-python">Python</li>
<li class="lang-lua">Lua</li>
</ul>
</div>
$(‘ul.lang>li.lang-javascript‘); // 可以选出[<li class="lang-javascript">JavaScript</li>]
$(‘div.testing>li.lang-javascript‘); // [], 无法选出,因为<div>和<li>不构成父子关系
过滤器
过滤器通常结合选择器一起使用
$(‘ul.lang li‘); // 选出JavaScript、Python和Lua 3个节点
$(‘ul.lang li:first-child‘); // 仅选出JavaScript
$(‘ul.lang li:last-child‘); // 仅选出Lua
$(‘ul.lang li:nth-child(2)‘); // 选出第N个元素,N从1开始
$(‘ul.lang li:nth-child(even)‘); // 选出序号为偶数的元素
$(‘ul.lang li:nth-child(odd)‘); // 选出序号为奇数的元素
查找
通常在某个节点的子节点中查找。使用find()方法,可以接受任何一个选择器。
例如:
<!-- HTML结构 -->
<ul class="lang">
<li class="js dy">JavaScript</li>
<li class="dy">Python</li>
<li id="swift">Swift</li>
<li class="dy">Scheme</li>
<li name="haskell">Haskell</li>
</ul>
1、向下查找:
var ul = $(‘ul.lang‘); // 获得<ul>
var dy = ul.find(‘.dy‘); // 获得JavaScript, Python, Scheme
var swf = ul.find(‘#swift‘); // 获得Swift
var hsk = ul.find(‘[name=haskell]‘); // 获得Haskell
2、向上查找,使用parent()方法:
var swf = $(‘#swift‘); // 获得Swift
var parent = swf.parent(); // 获得Swift的上层节点<ul>
var a = swf.parent(‘.red‘); // 获得Swift的上层节点<ul>,同时传入过滤条件。如果ul不符合条件,返回空jQuery对象
3、同层级的查找,使用prev()和next()方法
var swift = $(‘#swift‘);
swift.next(); // Scheme
// 返回空的jQuery对象,因为Swift的下一个元素为Scheme不符合条件[name=haskell]
swift.next(‘[name=haskell]‘);
swift.prev(); // Python
swift.prev(‘.dy‘); // Python,因为Python同时符合过滤器条件.dy
过滤
方法可以过滤掉不符合选择器条件的节点:
var langs = $(‘ul.lang li‘); // 拿到JavaScript, Python, Swift, Scheme和Haskell
var a = langs.filter(‘.dy‘); // 拿到JavaScript, Python, Scheme
//函数内部的被绑定为DOM对象,不是jQuery对象
var langs = $(‘ul.lang li‘); // 拿到JavaScript, Python, Swift, Scheme和Haskell
langs.filter(function () {
// this指的是标签为li的DOM对象
return this.innerHTML.indexOf(‘S‘) === 0; // 返回S开头的节点
}); // 拿到Swift, Scheme
方法把一个jQuery对象包含的若干DOM节点转化为其他对象:
var langs = $(‘ul.lang li‘); // 拿到JavaScript, Python, Swift, Scheme和Haskell
var arr = langs.map(function () {
return this.innerHTML;
}).get(); // 用get()拿到包含string的Array:[‘JavaScript‘, ‘Python‘, ‘Swift‘, ‘Scheme‘, ‘Haskell‘]
3、一个jQuery对象如果包含了不止一个DOM节点,、和方法可以返回一个新的jQuery对象,把不需要的DOM节点去掉:
var langs = $(‘ul.lang li‘); // 拿到JavaScript, Python, Swift, Scheme和Haskell
var js = langs.first(); // JavaScript,相当于$(‘ul.lang li:first-child‘)
var haskell = langs.last(); // Haskell, 相当于$(‘ul.lang li:last-child‘)
var sub = langs.slice(2, 4); // Swift, Scheme, 参数和数组的slice()方法一致
总结:
$("")是一个jquery对象,而不是一个dom element
value是dom element的属性
jquery与之对应的是val
val() :获得第一个匹配元素的当前值。
val(val):设置每一个匹配元素的值。
所以,代码应该这样写:
取值:val = $("#id")[0].value; 或者$("#id").val();
赋值: $("#id")[0].value = "new
value"; 或者$("#id").val("new value");
[SqlException (0x80131904): 在从服务器接收结果时发生传输级错误。 (provider: Session Provider, error: 19 - 物理连接不可用)]
System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) +2552942
System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) +5952492
System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) +285
System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error) +615
System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync() +191
System.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket() +104
System.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer() +95
System.Data.SqlClient.TdsParserStateObject.TryReadByte(Byte& value) +41
System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) +510
System.Data.SqlClient.SqlDataReader.TryConsumeMetaData() +58
System.Data.SqlClient.SqlDataReader.get_MetaData() +89
System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted) +430
System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest) +2598
System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry) +1483
System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +64
System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +240
System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) +41
System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior) +12
System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior) +435
[EntityCommandExecutionException: 执行命令定义时出错。有关详细信息,请参阅内部异常。]
Storebook.CMS.Business.Prepare.BFContract.GetByContractId(BLOContract context) in D:storebookStorebook.CMS.Business.PrepareBFContract.cs:579
BasePage.GetCurrentContractIsShowEDW(String contractId) in d:storebookStorebook.CMS.WebApp_CodeBaseBasePage.cs:2773
UserControl_Perform_PaymentList.bindHistoryList() in d:storebookStorebook.CMS.WebUserControlPerformPaymentList.ascx.cs:264
UserControl_Perform_PaymentList.Page_Load(Object sender, EventArgs e) in d:storebookStorebook.CMS.WebUserControlPerformPaymentList.ascx.cs:94
System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +51
System.Web.UI.Control.OnLoad(EventArgs e) +95
System.Web.UI.Control.LoadRecursive() +59
System.Web.UI.Control.LoadRecursive() +131
System.Web.UI.Control.LoadRecursive() +131
System.Web.UI.Control.LoadRecursive() +131
System.Web.UI.Control.LoadRecursive() +131
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +678
开发中会遇到BOM头, 导致程序无法执行. 浏览器返回接口如下图:
去除BOM头解决方法:
<?php
ini_set(‘memory_limit‘,‘1024M‘);
function checkdir($basedir) {
if ($dh = opendir ( $basedir )) {
while ( ($file = readdir ( $dh )) !== false ) {
if ($file != ‘.‘ && $file != ‘..‘) {
if (! is_dir ( $basedir . "/" . $file )) { // 如果是文件
echo "filename: $basedir/$file " . checkBOM ( "$basedir/$file" ) . " <br>";
} else {
$dirname = $basedir . "/" .$file; // 如果是目录
checkdir ( $dirname );
}
}
}
closedir ( $dh );
}
}
function checkBOM($filename) {
global $auto;
$contents = file_get_contents ( $filename );
$charset [1] = substr ( $contents, 0, 1 );
$charset [2] = substr ( $contents, 1, 1 );
$charset [3] = substr ( $contents, 2, 1 );
if (ord ( $charset [1] ) == 239 && ord ( $charset [2] ) == 187 && ord ( $charset [3] ) == 191) { // BOM 的前三个字符的ASCII 码分别为 239 187 191
if ($auto == 1) {
$rest = substr ( $contents, 3 );
rewrite ( $filename, $rest );
return ("<font color=red>BOM found, automatically removed.</font>");
} else {
return ("<font color=red>BOM found.</font>");
}
} else
return ("BOM Not Found.");
}
function rewrite($filename, $data) {
$filenum = fopen ( $filename, "w" );
flock ( $filenum, LOCK_EX );
fwrite ( $filenum, $data );
fclose ( $filenum );
}
$auto=1;
$dir=‘D:/web/‘;//项目文件路径
checkDir($dir);
?>
jQuery的属性操作模块总共有4个部分,本篇说一下第3个部分:类样式操作部分,用于修改DOM元素的class特性的,对于类样式操作来说,jQuery并没有定义静态方法,而只定义了实例方法,如下:
addClass(value) ;为匹配元素集合中的每个元素添加一个或多个类样式,通过修改DOM属性className来修改类样式,value可以是个以空格分隔的类样式或者一个函数(返回一个或多个以空格分隔的类样式)
hasClass(selector) ;检测匹配元素中的任意元素是否含有指定的类样式,只要其中一个元素含有就返回true,selector是一个类样式。
removeClass(value) ;从匹配元素集合中的每个元素上移除一个或多个或全部类样式,value可以为空(全部移除)、以空格分隔的类样式(移除多个样式),或者是个函数(该函数返回一个或多个以空格分隔的类样式)
toggleClass(value,stateVal) ;对设置或移除被选元素的一个或多个类进行切换,有五种用法
writer by:大沙漠 QQ:22969969
·toggleClass() ;未传入参数 ;这时如果当前元素含有样式则移除所有类,如果没有则尝试恢复。
·toggleClass(stateVal) ;只传入一个布尔值类型 ;如果stateVal是true,则等同于toggleClass();如果是false则总是移除所有类。
·toggleClass(value) ;参数1是字符串或函数,未传入参数2 ;value是字符串时表示一个或多个样式,用空格分隔,下同;value是函数时返回字符串格式。如果匹配元素含有指定的类样式,则移除,否则添加该样式。
·toggleClass(value,stateVal) ;参数1是字符串或函数,参数2是布尔值 ;当stateVal是true时总是添加,是false时则总是移除
对于toggleClass切换样式时,jQuery内部实现会将所有类暂时保存在数据缓存对象的__className__数据中,等下次恢复时尝试读取。
举个栗子吧:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
<style>
.color{color: #f00;}
.back{background: #ccc;}
</style>
</head>
<body>
<p>Hello World!</p>
<button id="b1">设置所有样式</button>
<button id="b2">切换color样式</button>
<button id="b3">取消所有样式</button>
<script>
let b1 = document.getElementById(‘b1‘),
b2 = document.getElementById(‘b2‘),
b3 = document.getElementById(‘b3‘);
b1.onclick = ()=>{
$(‘p‘).addClass(‘color back‘); //添加所有样式
}
b2.onclick = ()=>{
$(‘p‘).toggleClass(‘color‘); //切换color样式
}
b3.onclick = ()=>{
$(‘p‘).removeClass(); //取消所有样式
}
</script>
</body>
</html>
这里我们在style自定义了两个class:color和back,前者修改字体的颜色,后者修改字体的背景色,然后定义了三个按钮分别用于操作样式,渲染如下:
点击设置所有样式,将在p这个DOM元素上添加color和back两个class,如下:
当点击切换color样式时,由于p元素上的color这个class已经存在了,因此会取消掉:
当再次点击切换color样式这个按钮时,由于p元素上的color这个class已经取消了,因此此时会显示出来,如下:
当我们点击取消所有样式时将会把p元素上的所有class给删掉,又回到初始状态,如下:
源码分析
jQuery内部对于样式的操作是修改对应DOM元素的className属性来实现的,中间通过字符串的indexOf和replace操作实现查找和替换,对于添加样式和删除样式的逻辑如下:
jQuery.fn.extend({
addClass: function( value ) { //添加类样式
var classNames, i, l, elem,
setClass, c, cl;
if ( jQuery.isFunction( value ) ) { //如果value是函数
return this.each(function( j ) {
jQuery( this ).addClass( value.call(this, j, this.className) ); //在每个匹配元素上执行该函数并且取其返回值作为待添加的类样式,然后调用.addClass(className)添加类样式。执行函数时,传入两个参数,分别是元素在集合中的下标位置和当前样式值。
});
}
if ( value && typeof value === "string" ) { //如果value是字符串,可以是空格分隔的多个样式
classNames = value.split( rspace ); //用/s+/对value进行分隔
for ( i = 0, l = this.length; i < l; i++ ) { //遍历匹配元素
elem = this[ i ];
if ( elem.nodeType === 1 ) { //只针对元素节点
if ( !elem.className && classNames.length === 1 ) { //如果elem.className不存在,且待添加的样式个数为1,则直接设置elem.className
elem.className = value;
} else {
setClass = " " + elem.className + " "; //在待添加的类样式className前后加空格
for ( c = 0, cl = classNames.length; c < cl; c++ ) { //历要添加的类样式value
if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { //如果classNames中的某个样式不存在setClass中
setClass += classNames[ c ] + " "; //则添加该样式
}
}
elem.className = jQuery.trim( setClass ); //最后去除类样式两边的空格,再设置到elem.className中
}
}
}
}
return this;
},
removeClass: function( value ) { //从匹配元素集合中的每个元素上移除一个或多个或全部类样式。通过修改DOM属性className来移除类样式。
var classNames, i, l, elem, className, c, cl;
if ( jQuery.isFunction( value ) ) { //如果value是一个函数
return this.each(function( j ) { //遍历匹配元素
jQuery( this ).removeClass( value.call(this, j, this.className) ); //调用每个匹配元素的removeClass()函数,参数是value函数的返回值
});
}
if ( (value && typeof value === "string") || value === undefined ) { //如果value存在且是一个字符串 或者 value未定义
classNames = ( value || "" ).split( rspace ); //用空白符分隔value以支持一次移除多个类样式。这里如果value是空的,那么结果是[""],该数组的length等于1
for ( i = 0, l = this.length; i < l; i++ ) { //遍历匹配元素
elem = this[ i ]; //elem是匹配的元素
if ( elem.nodeType === 1 && elem.className ) { //只支持已经设置了className的元素节点。
if ( value ) { //如果传入了value值,则表示是设置样式
className = (" " + elem.className + " ").replace( rclass, " " ); //classNames是当前元素的类样式,两边加个空格,再去除出换行、制表、回车符。var rclass = /[
]/g,
for ( c = 0, cl = classNames.length; c < cl; c++ ) { //遍历要移除的类样式数组classNames
className = className.replace(" " + classNames[ c ] + " ", " "); //调用字符串方法replace()逐个从当前类样式中移除
}
elem.className = jQuery.trim( className ); //去掉空白字符,在设置elem.className属性
} else {
elem.className = "";
}
}
}
}
return this;
},
/*略*/
})
当我们在页面里做各种动画效果时还是会用到这个API的,挺方便的,配合css可以实现各种的动画效果。
1 <!DOCTYPE html>
2 <html lang="en">
3
4 <head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <meta http-equiv="X-UA-Compatible" content="ie=edge">
8 <title>表单元素input,button</title>
9 </head>
10
11 <body>
12 <h2>form常见结构</h2>
13 <form action="" method="post" enctype="multipart/form-data">
14 <input type="text">
15 <button>提交</button>
16 </form>
17 <hr>
18
19 <h2>自动完成</h2>
20 <form action="" autocapitalize="off">
21 <input type="text">
22 <input type="text" autocapitalize="off">
23 </form>
24 <hr>
25
26 <h2>输入框文字说明</h2>
27 <form action="">
28 用户名:<input type="text">
29 <br>
30 密码:<input type="text">
31 </form>
32 <hr>
33
34 <h2>输入框文字说明,使用lable</h2>
35 <form action="">
36 <label for="username">
37 用户名:<input type="text" id="username">
38 </label>
39 <br>
40 <label for="password">
41 密码:<input type="text" id="password">
42 </label>
43 </form>
44 <hr>
45
46 <h2>自动聚焦,光标会定位到这</h2>
47 <form action="">
48 <input type="text" autofocus>
49 </form>
50 <hr>
51
52 <h2>禁止表单</h2>
53 <form action="">
54 <input type="text" disabled>
55 </form>
56 <hr>
57
58 <h2>fieldset进行分组</h2>
59 <form action="">
60 <fieldset>
61 <legend>个人信息</legend>
62 <p>
63 <label for="">
64 姓名<input type="text">
65 </label>
66 </p>
67 <p><label for="">
68 年龄<input type="text">
69 </label>
70 </p>
71 </fieldset>
72 <fieldset>
73 <p>联系方式</p>
74 <p>
75 <label for="">
76 QQ<input type="text">
77 </label>
78 </p>
79 <p>
80 <label for="">
81 WX<input type="text">
82 </label>
83 </p>
84 </fieldset>
85 <button>提交</button>
86 <button type="reset">重置</button>
87 </form>
88 <hr>
89
90 <h2>input的各种属性</h2>
91 <form action="">
92 <p>
93 <label for="">
94 姓名<input type="text" minlength="2" maxlength="12" placeholder="至少2个字符">
95 </label>
96 </p>
97 <p><label for="">
98 年龄<input type="text" minlength="1" size="3" maxlength="3">
99 </label>
100 </p>
101 <button>提交</button>
102 </form>
103 <hr>
104
105 <h2>input的datalist</h2>
106 <form action="">
107 <input type="text" list="fruits">
108 <datalist id="fruits">
109 <option value="香蕉"></option>
110 <option value="苹果"></option>
111 <option value="菠萝"></option>
112 </datalist>
113 </form>
114 <hr>
115
116 <h2>input的权限</h2>
117 <form action="">
118 <input type="text" disabled placeholder="被禁止输入">
119 <input type="text" readonly value="这是只读">
120 </form>
121 <hr>
122
123 <h2>密码</h2>
124 <form action="">
125 <p>
126 <input type="text">
127 </p>
128 <p><input type="password" placeholder="密码需要大小写字母和数字">
129 </p>
130 </form>
131 <hr>
132
133 <h2>把input变成按钮</h2>
134 <form action="">
135 <input type="submit">
136 <input type="reset">
137 <input type="button" value="确认按钮">
138 </form>
139 <hr>
140
141 <h2>数字类型</h2>
142 <form action="">
143 <input type="number" min="2" max="8" value="3" step="1" name="price">
144 <input type="range" min="2" max="8" value="3" step="1" name="price">
145 </form>
146 <hr>
147
148 <h2>复选框</h2>
149 <form action="">
150 <label for="">
151 是否同意<input type="checkbox" checked>
152 </label>
153 </form>
154 <hr>
155
156 <h2>单选按钮</h2>
157 <form action="">
158 <label for="">
159 香蕉<input type="radio" value="香蕉" name="fruit">
160 </label>
161 <label for="">
162 苹果<input type="radio" value="苹果" name="fruit">
163 </label>
164 <label for="">
165 菠萝<input type="radio" value="菠萝" name="fruit">
166 </label>
167 </form>
168 <hr>
169
170 <h2>输入邮箱、电话、网址</h2>
171 <form action="">
172 <label for="">
173 邮箱<input type="email" placeholder="username@abc.com">
174 </label>
175 <label for="">
176 电话<input type="tel" placeholder="138-1111-2222">
177 </label>
178 <label for="">
179 网址<input type="url" placeholder="http://edu.51cto.com">
180 </label>
181 <button>提交</button>
182 </form>
183 <hr>
184
185 <h2>时间和颜色的选择</h2>
186 <form action="">
187 <label for="">
188 <input type="date">
189 </label>
190 <label for="">
191 <input type="color" value="#10f0f0">
192 </label>
193 </form>
194 <hr>
195
196 <h2>隐藏的类型</h2>
197 <form action="">
198 <label for="">
199 用户名<input type="text">
200 </label>
201 <label for="">
202 密码<input type="text">
203 </label>
204 <label for="">
205 <input type="hidden">
206 </label>
207 </form>
208 <hr>
209
210 <h2>图片按钮</h2>
211 <form action="">
212 <input type="image" src="https://www.gxlsystem.com/images/accept.png" name="点击图片提交" alt="点击提交">
213 </form>
214 <hr>
215
216 <h2>上传文件</h2>
217 <form enctype="multipart/form-data">
218 <input type="file" name="文件1">
219 <br>
220 <button>上传</button>
221 </form>
222 <hr>
223
224 <h2>必填、正则域名验证</h2>
225 <form action="">
226 <label for="">
227 邮箱<input type="email" placeholder="username@abc.com" required pattern=".*@abc.com">
228 </label>
229 <label for="">
230 电话<input type="tel" placeholder="138-1111-2222" required>
231 </label>
232 <label for="">
233 网址<input type="url" placeholder="http://edu.51cto.com">
234 </label>
235 <button>提交</button>
236 </form>
237 <hr>
238
239 <h2>下拉框,选一个</h2>
240 <form action="">
241 <label for="fruit">你喜欢吃什么水果?
242 <select name="" id="fruit">
243 <option value="香蕉">香蕉</option>
244 <option value="苹果" selected>苹果</option>
245 <option value="菠萝">菠萝</option>
246 </select>
247 </label>
248 </form>
249 <hr>
250
251
252 <h2>下拉框,选多个</h2>
253 <form action="">
254 <label for="more">你喜欢吃什么水果?
255 <select name="" id="more" multiple>
256 <option value="香蕉">香蕉</option>
257 <option value="苹果" selected>苹果</option>
258 <option value="菠萝">菠萝</option>
259 </select>
260 </label>
261 </form>
262 <hr>
263
264 <h2>多行文本,写评论</h2>
265 <form action="">
266 <label for="comment">写个评论
267 <textarea name="" id="comment" cols="50" rows="5"></textarea>
268 </label>
269 </form>
270
271 </body>
272
273 </html>
第15章 Apache服务·
15.1Apache介绍
Apache是一款软件,有多种产品,可以支持SSL技术,支持多个虚拟主机。它快速、可靠并且可通过简单的API扩充,将Perl/Python等编译到服务器中。
Apache的特点是简单、速度快、性能稳定,并可做来使用。它可以在大多数计算机操作系统中运行,由于其跨平台和安全性被广泛使用。
Apache HTTP (简称Apache)是的一个开放源码的网页服务器,可以在大多数计算机操作系统中运行,由于其多平台和安全性被广泛使用,是最流行的Web服务器端软件之一。它快速、可靠并且可通过简单的API扩展,将Perl/Python等解释器编译到服务器中。 [2]
Apache HTTP服务器是一个模块化的服务器,源于NCSAhttpd服务器,经过多次修改,成为世界使用排名第一的软件。
它可以运行在几乎所有广泛使用的上。
Apache源于NCSAhttpd服务器,经过多次修改,成为世界上最流行的软件之一。Apache取自“a patchy server”的读音,意思是充满补丁的服务器,因为它是,所以不断有人来为它开发新的功能、新的特性、修改原来的缺陷。Apache的特点是简单、速度快、性能稳定,并可做来使用。
本来它只用于小型或试验Internet网络,后来逐步扩充到各种系统中,尤其对的支持相当完美。Apache有多种产品,可以支持技术,支持多个。Apache是以为基础的结构,进程要比消耗更多的系统开支,不太适合于多处理器环境,因此,在一个Apache Web站点扩容时,通常是增加或扩充群集节点而不是增加。到目前为止Apache仍然是世界上用的最多的Web服务器,市场占有率达60%左右。世界上很多著名的网站如、Yahoo!、W3 Consortium、Financial Times等都是Apache的产物,它的成功之处主要在于它的源代码开放、有一支开放的开发队伍、支持的应用(可以运行在几乎所有的Unix、Windows、Linux系统平台上)以及它的可移植性等方面。
Apache的诞生极富有戏剧性。当NCSAWWW服务器项目停顿后,那些使用NCSAWWW服务器的人们开始交换他们用于该服务器的补丁程序,他们也很快认识到成立管理这些补丁程序的论坛是必要的。就这样,诞生了Apache Group,后来这个团体在的基础上创建了Apache。
Apache软件拥有以下特性:
Apache Server配置界面
1.支持最新的/1.1通信协议
2.拥有简单而强有力的基于文件的配置过程
3.支持通用网关接口
4.支持基于IP和基于域名的虚拟主机
5.支持多种方式的认证
6.集成处理模块
7.集成模块
8.支持实时监视服务器状态和定制服务器日志
9.支持服务器端包含指令(SSI)
10.支持安全层(SSL)
11.提供用户会话过程的跟踪
12.支持FastCGI
13.通过模块可以支持Servlets
15.2 安装Apche
思路
1、wget http的包
2、安装依赖项,pcre zlib-devel pcre-devel gcc gcc-c++ apr-util
3、解压httpd的包,tar,unzip
4、https://www.gxlsystem.com/configure --prefix=路径 --user=用户 --group=组,--with加组件名
5.echo $?
6.make && make install
7.进入你httpd的目录,把apachectl加入环境变量,并启动
8、关闭selinux及防火墙
9、测试,宿主机去访问,或者curl 127.0.0.1:端口
安装过程:
安装httpd
[root@httpd ~]# yum install httpd -y
已加载插件:fastestmirror
Loading mirror speeds from cached hostfile
* base: mirror.jdcloud.com
* extras: mirrors.aliyun.com
* updates: mirrors.aliyun.com
软件包 httpd-2.4.6-90.el7.centos.x86_64 已安装并且是最新版本
无须任何处理
[root@httpd ~]# rpm -qa httpd
httpd-2.4.6-90.el7.centos.x86_64
卸载命令
[root@httpd ~]# rpm -e(--nodebse) httpd
[root@httpd ~]# rpm -qa httpd
[root@httpd ~]# wget http://www.apache.orghttps://www.gxlsystem.com/dist/httpd/httpd-2.4.39.tar.gz
今天刚重新注册了博客,好多年没写博客了。以后每周至少写三篇博客,至少1篇原创。
给大家推荐一些IT行业技术人必须学习成长的网站。
力扣 对算法、数据结构、搜索、数学方面应用感兴趣的,可以去学习
思否 中国的质量较高的技术社区 https://segmentfault.com/
人人都是产品经理 技术人升级之路必不可少 http://www.woshipm.com/
即时通讯网 对通讯感兴趣的必看
donews 互联网资讯网 http://www.donews.com/
36kr 互联网资讯网
github 大部分人都应该知道的开源平台 https://github.com/
后续持续。。。
问题描述:
There are servers numbered from to connected by undirected server-to-server forming a network where represents a connection between servers and . Any server can reach any other server directly or indirectly through the network.
A critical connection is a connection that, if removed, will make some server unable to reach some other server.
Return all critical connections in the network in any order.
Example 1:
Input: n = 4, connections = [[0,1],[1,2],[2,0],[1,3]]
Output: [[1,3]]
Explanation: [[3,1]] is also accepted.
Constraints:
There are no repeated connections.
解题思路:
这道题让我去找图中的桥(bridge)
我一开始的反应是先去找环,然后把构成环的边删除掉,剩下的就是桥。
可是找环也很麻烦,一开始想的是用字符串存储当前路径,并且查找有没有重复,但是这样空间占用多且有可能会出现死循环。
看了网上的解答,还是我对图的了解太少了。。。
这里由 提供的一些参考资料可以帮我们去了解概念
然后我还找了个视频看了一下:
主要思想是记录第一次进入这个节点的时间 entryTime[] 和 另外一个标志时间low,我这里也没有想好怎么解释这个数组,我认为是检查是否出现别的路径可以从已经访问过的节点到达该节点标志,这也是我们用来找桥的重要标志。
对每一个节点 i ,但我们第一次访问的时候,用全局变量time赋值给entryTime[i] 和 low[i], 然后dfs去访问他的孩子节点。
这里需要注意的是,因为是无向图,我们访问的孩子节点不能是它的父亲节点。
对于它的孩子节点 j ,有两种可能:
1. 未访问过
对孩子节点 j 进行dfs
对孩子节点j dfs 结束后需要比较entryTime[i] 和 low[j]的关系。如果entryTime[i] < low[j] 则说明,j节点无法连接到i的前继节点,则该边为桥。
2. 访问过
比较该点的entryTime[j] 和 low[i], 如果low[i] > entryTime[j], 我们需要更新 low[i] = entryTime[j], 这个时候实际上是出现了back edge,在节点i我们可以有另一条路径到出现时间为j的节点,环就出现了。
代码:
class Solution {
public:
vector<vector<int>> criticalConnections(int n, vector<vector<int>>& connections) {
vector<vector<int> > adj(n);
for(auto c : connections){
adj[c[0]].push_back(c[1]);
adj[c[1]].push_back(c[0]);
}
int time = 0;
vector<int> entry(n, -1);
vector<int> low(n, -1);
vector<vector<int> > ret;
dfs(0, adj, time, entry, low, ret, -1);
return ret;
}
private:
void dfs(int curNode, vector<vector<int> > &g, int &time, vector<int> &entryTime,
vector<int> &low, vector<vector<int>> &ret, int parentNode){
if(entryTime[curNode] == -1){
entryTime[curNode] = time;
low[curNode] = entryTime[curNode];
++time;
}
for(auto &child : g[curNode]){
if(child == parentNode) continue;
if(entryTime[child] == -1){
dfs(child, g, time, entryTime, low, ret, curNode);
low[curNode] = min(low[curNode], low[child]);
if(low[child] > entryTime[curNode]){
ret.push_back({curNode,child});
}
}else{
low[curNode] = min(low[curNode], entryTime[child]);
}
}
}
};
题目链接:https://vjudge.net/problem/POJ-1797
题意:n个点,m条带权边,求点1到点n的所有路径中最小边的最大值。
思路:
和poj2253一样,只不过那题n<=200,可以用floyd,而这题floyd会TLE,所以用dijkstra来做。
提一下floyd的做法,用dp[i][j]表示i到j的所有路径中最小边的最大值,那么转移方程是: dp[i][j]=max(dp[i][j] , min(dp[i][k] , dp[k][j]) )。
dijkstra的做法也是一样的,修改dis数组的定义,即dis[i][j]表示i到j的路径中最小边的最大值,那么松弛操作就是:dis[j]=max(dis[j] , min(dis[i] , e[i][j]) )。优先队列采用降序队列,因为是求最大值,还有dis数组初始化-inf,dis[1]=inf。
AC代码:
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=1005;
const int inf=0x3f3f3f3f;
int T,n,m,cas,e[maxn][maxn],vis[maxn],dis[maxn];
typedef pair<int,int> PII;
priority_queue<PII> pq;
void dijkstra(){
int num=0;
for(int i=1;i<=n;++i)
vis[i]=0,dis[i]=-inf;
dis[1]=inf;
pq.push(make_pair(inf,1));
while(!pq.empty()&&num<=n){
int u=pq.top().second;
pq.pop();
if(vis[u]) continue;
vis[u]=1;
++num;
for(int i=1;i<=n;++i)
if(e[u][i]!=-1&&!vis[i]){
dis[i]=max(dis[i],min(dis[u],e[u][i]));
pq.push(make_pair(dis[i],i));
}
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
e[i][j]=-1;
for(int i=1;i<=m;++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
e[u][v]=e[v][u]=w;
}
dijkstra();
printf("Scenario #%d:
",++cas);
printf("%d
",dis[n]);
}
return 0;
}
虚拟Web主机
在同一台物理服务器中运行多个Web站点,其中每一一个站点并不独立占用一台真正的计算机。
httpd支持的虚拟主机类型
基于域名的虚拟主机
基于IP地址的虚拟主机
基于端口的虚拟主机
构建虚拟主机------基于域名
(1)安装bind、httpd服务。
(2)进入named服务的主配置文件,将下图两个位置改为“any”。
(3)进入named服务的区域配置文件,添加两个域名的区域信息。
(4)进入“/var/named/”目录,保留权限复制一份“named.localhost”区域数据配置文件,命名为“aaa.com.zone”,然后对其进行修改。
(5)再保留权限复制一份“aaa.com.zone”文件,命名为“bbb.com.zone”,不用进行修改。然后开启named服务,关闭防火墙和增强性安全功能。
(6)再开一台win10虚拟机,将其DNS服务器的IP地址,设置为刚才Linux系统的IP地址。
(7)用win10主机去测试DNS服务能否解析,解析成功。
(8)进入“/etc/httpd/conf”目录,创建一个“extra/”目录,然后进入“extra/”目录,用vim编辑器,新建一个配置文件“vhost.conf”,在配置文件里输入以下内容。
(9)进入“/var/www/html/”创建两个目录“aaa/”、“bbb/”。
(10)进入“aaa/”目录,新建一个站点首页文件,内容如下:
(11)进入“bbb/”目录,新建一个站点首页文件,内容如下:
(12)进入httpd服务的主配置文件,在末行将我们新建的配置文件写进主配置文件,然后启动httpd服务。
(13)用win10 主机去分别访问两个域名,都能访问成功。
构建虚拟主机------基于端口
(1)在上一个实验的基础上,进入配置文件“vhost.conf”,添加一个“www.aaa.com”域名的8080端口。
(2)进入“/var/www/html”目录,新建一个“aaa02”目录,进入“aaa02”目录,新建一个站点首页文件,内容如下:
(3)进入httpd服务主配置文件,添加监听端口,同时将IPv6的端口的监听注销。重启httpd服务。
(4)再次用win10主机访问两个端口不同的域名,访问成功。
构建虚拟主机------基于IP
(1)给Linux主机添加一块网卡,查看IP地址。
(2)进入配置文件“vhost.conf”,进行如下输入:
(3)分别对“aaa”站点与“aaa02”站点的主页文件进行如下修改:
(4)进入httpd主配置文件,进行端口的添加与注释。然后重启httpd服务。
(5)用win10主机去访问两个不同IP地址的站点,访问成功。但是只能用IP地址访问,一般情况访问网站用的是域名,接下来我们进行域名访问不同IP地址站点的配置。
(6)首先在配置文件“vhost.conf”中添加,域名“ServerName”。
(7)进入named服务的区域配置文件中,添加一个“aaa02”的区域信息。
(8)进入“/var/named/”目录,保留权限复制一份“aaa.com.zone”文件,命名为“aaa02.com.zone”,同时对其进行如下修改:
(9)再次用win10主机,通过域名去访问两个不同IP地址的站点,访问成功。
原生js实现上拉加载:
原生js实现上拉加载其实超级简单,把原理整明白了你也会,再也不用去引一个mescroll啦~
好了,废话不多说,开始进入正题:上拉加载是怎么去做的,原理就是监听滚动条滑到页面底部,然后就去做一次请求数据。那么只要我们对滚动监听即可。
现在,让我们来看代码:
window.onscroll = function () {
var scrollH = document.documentElement.scrollHeight;// 文档的完整高度
var scrollT = document.documentElement.scrollTop || document.body.scrollTop; // 当前滚动条的垂直偏移
var screenH = window.screen.height; // 屏幕可视高度
if ((scrollH - scrollT - screenH) < 5) { //5只是一个相对值,可以让页面再接近底面的时候就开始请求
// 执行请求
}
};
温馨提醒:做好防抖以及判断没有数据的情况。
/**
* for js、vue: 格式化人名,可用于默认头像
**/
function userNameFormat(value) {
if (typeof(value) == "undefined" || value == null || value == ‘‘) {
return value;
}
if (value.length <= 1) {
return value;
}
value = value.substr((value.length - 2), value.length)
return value;
};
在项目中,经常会遇到一些涉及到拖拽的需求,github上面有一个开源的SortableJS的插件,支持Vue,React,Angular等多种框架,实现效果很好,基本可以满足大部分的需求,下面就第一次在jquery中的使用做个简单的demo记录。
引入文件Sortable.min.js
指定包裹容器的id
根据api开始创建使用即可
官方文档地址:
demo演示地址:
.
[ArgumentNullException: The Value of property CloneFrom can not be empty string!
参数名: CloneFrom]
Wuqi.Webdiyer.AspNetPager.set_CloneFrom(String value) +177
ASP.offereemanage_offereequerylist_aspx.__BuildControlschedulePage() in d:storebookStorebook.CMS.WebOffereeManageOffereeQueryList.aspx:234
ASP.offereemanage_offereequerylist_aspx.__BuildControldiv1() in d:storebookStorebook.CMS.WebOffereeManageOffereeQueryList.aspx:159
ASP.offereemanage_offereequerylist_aspx.__BuildControl__control61(Control __ctrl) in d:storebookStorebook.CMS.WebOffereeManageOffereeQueryList.aspx:139
System.Web.UI.CompiledTemplateBuilder.InstantiateIn(Control container) +15
AjaxControlToolkit.TabPanel.OnInit(EventArgs e) +117
System.Web.UI.Control.InitRecursive(Control namingContainer) +139
System.Web.UI.Control.InitRecursive(Control namingContainer) +312
System.Web.UI.Control.InitRecursive(Control namingContainer) +312
System.Web.UI.Control.InitRecursive(Control namingContainer) +312
System.Web.UI.Control.InitRecursive(Control namingContainer) +312
System.Web.UI.Control.InitRecursive(Control namingContainer) +312
System.Web.UI.Control.InitRecursive(Control namingContainer) +312
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +408
HTML元素
在基础知识一种写过元素由开始标签、元素内容、结束标签所构成,如:<p>这是一个段落</p>就是一个段落元素
其实简单的来说HTML文档就是由嵌套的HTML元素构成的。
例如:一个简单的HTML文档
<!DOCTYPE html>
<html>
<body>
<p>这是第一个段落。</p>
</body>
</html>
上实例就包括了三个HTML元素分别是:<html>元素<body>元素<p>元素
注意:
*有些标签可以完成自结束的功能,例如:<br/>
*虽然不加结束标签时大多数浏览器也会正确地显示 HTML,但是忘记使用结束标签有时也会产生不可预料的结果或错误。
*HTML 标签对大小写不敏感:<P> 等同于 <p>。许多网站都使用大写的 HTML 标签。但是我们在开发时一般都使用小写
HTML属性
HTML属性可以理解为是HTML元素提供的附加信息,他一般描述于开始标签中,一般以名值对的形式出现 ,如name=“value”
关于HTML属性的一般注意事项:
**属性值应该始终包括在引号内,双引号是最常用的,但是在某些个别情况下如:属性值自身就包含了双引号,那这个时候就要用使用单引号了如:name=‘xiao"hong"mao‘
**属性和属性值对大小写不敏感,但是新版本的HTML要求使用小写属性
常用的属性有:
属性
描述
class
为html元素定义一个或多个类名(classname)(类名从样式文件引入)
id
定义元素的唯一id
style
规定元素的行内样式(inline style)
title
描述了元素的额外信息 (作为工具条使用)
*HTML属性有很多,在我们使用时可以查找HTML属性参考手册
原因:
你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容插值。
解决办法:
使用 标签替换掉 标签。
利用的就是 标签的这个功能:被包围在 元素中的文本通常会保留空格和换行符,并且文本也会呈现为等宽字体。
注意:
pre标签使用的时候会出现不自动换行的问题,而且存在一些默认样式。
一下是解决自动换行的办法:
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
本文引用:
文本属性
介绍几个常用的。
文本对齐
text-align 属性规定元素中的文本的水平对齐方式。
属性值:none | center | left | right | justify
文本颜色
color属性
文本首行缩进
text-indent 属性规定元素首行缩进的距离,单位建议使用em
文本修饰
text-decoration属性规定文本修饰的样式
属性值:none(默认) | underline(下划线) | overline(定义文本上的一条线) | line-through (定义穿过文本下的一条线) | inherit(继承父元素的text-decoration属性的值。)
行高
line-height就是行高的意思,指的就是一行的高度。
阴影
text-shadow:水平方向偏移量 垂直方向偏移量 模糊度 阴影颜色;
文字溢出
text-overflow:clip | ellipsis
案例:文字超出部分显示...
css部分
div{
width: 200px;
height: 200px;
border: 1px solid red;
}
div p{
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
html部分
<div>
<p>变有钱 我变有钱多少人没日没夜地浪费时间</p>
<p>变有钱 我变有钱多少人没日没夜地浪费时间</p>
<p>变有钱 我变有钱多少人没日没夜地浪费时间</p>
</div>
字体属性
字体大小
font-size表示设置字体大小,如果设置成表示继承父元素的字体大小值。
字体粗细
font-weight表示设置字体的粗细
属性值:none(默认值,标准粗细) | bold(粗体) | border(更粗)| lighter(更细) | 100~900(设置具体粗细,400等同于normal,而700等同于bold)| inherit(继承父元素字体的粗细值)
字体系列
font-family
font-family: "Microsoft Yahei", "微软雅黑", "Arial", sans-serif
如果浏览器不支持第一个字体,则会尝试下一个。浏览器会使用它可识别的第一个值。如果都不支持则显示宋体。
行高 line-height
针对单行文本垂直居中
公式:行高的高度等于盒子的高度,可以使当行文本垂直居中,注意只适用单行文本。
针对多行文本垂直居中
行高的高度不能小于字体的大小,不然上下字之间会紧挨一起。
第一步,一个宽度300*300的盒子,看盒子中一共显示了几行文字,假如是5行,再看一下行高,如果行高是line-height:30px; 那么就知道行高*5=150px
第二步,让(盒子的高度-150px)/2=75;那么设置盒子的padding-top:75px;同时保证盒子的高度为300px,那么高度改为225px;
font-family
使用font-family注意几点:
1.网页中不是所有字体都能用哦,因为这个字体要看用户的电脑里面装没装,
比如你设置: font-family: "华文彩云"; 如果用户电脑里面没有这个字体,
那么就会变成宋体
页面中,中文我们只使用: 微软雅黑、宋体、黑体。
如果页面中,需要其他的字体,那么需要切图。 英语:Arial 、 Times New Roman
2.为了防止用户电脑里面,没有微软雅黑这个字体。
就要用英语的逗号,隔开备选字体,就是说如果用户电脑里面,
没有安装微软雅黑字体,那么就是宋体:
font-family: "微软雅黑","宋体"; 备选字体可以有无数个,用逗号隔开。
3.我们要将英语字体,放在最前面,这样所有的中文,就不能匹配英语字体,
就自动的变为后面的中文字体:
font-family: "Times New Roman","微软雅黑","宋体";
4.所有的中文字体,都有英语别名,
我们也要知道: 微软雅黑的英语别名:
font-family: "Microsoft YaHei";
宋体的英语别名: font-family: "SimSun";
font属性能够将font-size、line-height、font-family合三为一: font:12px/30px "Times New Roman","Microsoft YaHei","SimSun";
5.行高可以用百分比,表示字号的百分之多少。
一般来说,都是大于100%的,因为行高一定要大于字号。
font:12px/200% “宋体” 等价于 font:12px/24px “宋体”;
反过来,比如: font:16px/48px “宋体”;
等价于 font:16px/300% “宋体”
vertical-align
定义:vertical-align 属性设置元素的垂直对齐方式。
该属性定义行内元素的基线相对于该元素所在行的基线的垂直对齐
常用属性值:
baseline 默认值
top
bottom
middle
它的作用:
内联元素之间的对齐
文字与图片垂直方向的对齐
图片与托片垂直方向的对齐
行内块元素垂直方向的对齐
单元格td的内容垂直方向的对齐
论文地址:
一、相关工作
1、FCN
FCN开创了语义分割任务的先河,高级语义信息在分割网络中起着至关重要的作用。为了提取高级信息,FCN使用多个池化层来增加输出神经元的接受域大小。然而,增加池化层的数量会导致feature map的大小减小,这对将分割输出上采样回全分辨率造成了严重的挑战。为了解决较大的特征图分辨率和较大的接收域之间的矛盾,提出了一种新的空洞卷积算法。
2、空洞卷积(Atrous convolution )
与传统的卷积算子相比,atrous卷积能够在不增加核参数数目的情况下获得更大的接受域大小。由atrous卷积产生的feature map可以与输入的大小相同,但是每个输出神经元拥有更大的接受域,因此可以编码更高层次的语义。虽然atrous convolution解决了feature map分辨率与接受域大小之间的矛盾,atrouss - convolution输出的 feature map中的所有神经元都具有相同的接受域大小,这意味着语义掩码的生成过程只利用了单一尺度上的特征。可是,多尺度信息将有助于解决模糊情况,并产生更稳健的分类结果。
3、ASPP
为此,ASPP[2,3]提出将不同扩张率下的atrous convolution生成的feature map串联起来,使得输出feature map中的神经元包含多个接受域大小,对多尺度信息进行编码,最终提高性能。
然而,随着膨胀率的增加(如d > 24), atrous convolution变得越来越无效,逐渐失去了建模能力。因此,设计一个能够编码多尺度信息,同时又能获得足够大的接收域的网络结构是非常重要的。
二、DenseASPP
DenseASPP由一个基础网络和一系列层叠的卷积层组成。提出的DenseASPP结合了并行和级联使用空洞卷积层的优点,在更大的范围内产生了更多的尺度特征。通过一系列的特征连接,每个中间特征图上的神经元从多个尺度对语义信息进行编码,不同的中间特征图从不同的尺度范围对多尺度信息进行编码。通过一系列的空洞卷积,较晚层次的神经元获得越来越大的感受野,而不会出现ASPP的核退化问题。因此,DenseASPP最终的输出特征图不仅涵盖了大范围的语义信息,而且以非常密集的方式覆盖了该范围。
1、贡献
1) DenseASPP能够生成覆盖非常大范围的特性(就接受域大小而言)。
2) DenseASPP生成的特征能够非常密集地覆盖上述尺度范围。
2、工作模式
空洞卷积层以级联方式组织,每一层的膨胀率逐层增加。膨胀率小的层在下部,膨胀率大的层在上部。将每一层的输出与输入的feature map和较低层的所有输出连接起来,并将这些连接起来的feature map送入下一层。DenseASPP的最终输出是由多空洞率、多尺度的卷积生成的特征图。提出的结构可以同时组成一个更密集和更大的特征金字塔,只需要几个空洞卷积层。与原始的ASPP[3]相比,DenseASPP将所有空洞卷积层堆叠在一起,并用紧密的连接将它们连接起来。这种变化主要给我们带来两个好处:更密集的特征金字塔和更大的接受域。
3、优点
3.1密度特征金字塔
“密度”一词不仅表示特征金字塔的尺度多样性更好,还表示卷积涉及的像素比ASPP中更多。
密集抽样规模:DenseASPP是一个有效的架构,可以对不同规模的输入进行采样。DenseASPP的一个关键设计是使用紧密的连接来实现不同膨胀率的不同层次的集成
对于扩张速率为d、核大小为K的空洞卷积层,其等效接受域大小为:
R = (d - 1) × (K - 1) + K
以d = 3的3×3卷积层为例,对应的接受域大小为7。
将两个卷积层叠加在一起可以得到更大的接受域。假设我们分别有两个滤波尺寸为K1和K2的卷积层,新的接受域为:
K = K1 + K2 - 1
例如,内核大小为7的卷积层与内核大小为13的卷积层叠加,接收域大小为19。
DenseASPP由包含扩张率为3,6,12,18的扩张卷积,每组数字的表示扩张率的组合,长度表示等效的卷积核大小,k表示实际的接收野,如下所示:
密集的像素采样:与ASPP相比,DenseASPP在特征金字塔的计算中涉及到更多的像素。ASPP采用4个膨胀率分别为6、12、18、24的卷积层构成特征金字塔。与相同接收域的传统卷积层相比,大扩张率的卷积层的像素采样率非常稀疏。在DenseASPP中,膨胀率逐层增加,因此,上层的卷积可以利用下层的特征,使像素采样更加密集。
3.2更大的接受域
DenseASPP带来的另一个好处是更大的接受域。Atrous convolutional layers在传统的ASPP中是并行工作的,而四个分支在前馈过程中是不共享任何信息的。与之相反,DenseASPP中的空洞卷积层通过跳过连接来共享信息。小扩展率和大扩展率的层之间是相互依赖的,其中前馈过程不仅会构成一个更密集的特征金字塔,而且会产生一个更大的过滤器来感知更大的上下文。
设Rmax为特征金字塔的最大接受域,函数RK,d为核大小为K、扩张率为d的卷积层的接受域,则ASPP的最大接受域为(6,12,18,
24)是:
Rmax = max [R3,6, R3,12, R3,18, R3,24]
= R3,24
= 51
而DenseASPP(6、12、18、24)的最大接受域是:
Rmax = R3,6 + R3,12 + R3,18 + R3,24 - 3
= 122
如此大的接受域可以为高分辨率图像中的大对象提供全局信息。例如,Cityscapes[4]的分辨率为2048×1024,而我们的分割网络最后的feature map为256×128。
DenseASPP(6、12、18、24)覆盖了122特征图,DenseASPP(3、6、12、18、24)覆盖了128的特征图。
如果在实体类上面标注了@Data 自动生成了get/set/toString方法之后,他默认生成格式是
Grade(id=1, name=‘this is a testDemo‘, students=[Student(id=101, name=zhangsan, age=16), Student(id=102, name=lisi, age=23)])
这里在内容的前面会自动生成该实体类的类名,而且真正的内容是用圆括号 ( ) 括起来的,而JSON则是用花括号 { } 括起来的
如果要将对象和Json进行交互的话,建议还是手动生成toString()方法,这样的话可以避免格式的问题造成异常
分享我开发一系列web工具
1、搜索工具:支持谷歌、冲鸭、百度,或许是很好的搜索工具
2、翻译工具:支持谷歌、百度、有道,欢迎体验对比反馈,希望能做的更好
3、今日新闻:每天2小时自动更新,关心国家大事,欢迎提意见,把这个功能做的更好,更合乎大家需求
4、娱乐开心:笑话,英语短语,拍打养生,边打发时间还能边学习
5、说大话,开心大话聊天吧
6、需求登记表,欢迎提出您的需求
7、可以收藏或者把这个URL地址设置为 手机浏览器首页,帮助你更好工作与娱乐的工具
https://go.readmorejoy.com
margin塌陷问题
当时说到了盒模型,盒模型包含着margin,为什么要在这里说margin呢?因为元素和元素在垂直方向上margin里面有坑。
我们来看一个例子:
html结构:
<div class="father">
<div class="box1"></div>
<div class="box2"></div>
</div>
css样式:
*{
padding: 0;
margin: 0;
}
.father{
width: 400px;
overflow: hidden;
border: 1px solid gray;
}
.box1{
width: 300px;
height: 200px;
background-color: red;
margin-bottom: 20px;}
.box2{
width: 400px;
height: 300px;
background-color: green;
margin-top: 50px;
}
当给两个标准流下兄弟盒子 设置垂直方向上的margin时,那么以较大的为准,那么我们称这种现象叫塌陷。没法解决,我们称为这种技巧叫“奇淫技巧”。记住这种现象,在布局垂直方向盒子的时候主要margin的用法。
当我们给两个标准流下的兄弟盒子设置浮动之后,就不会出现margin塌陷的问题。
margin:0 auto;
div{
width: 780px;
height: 50px;
background-color: red;
/*水平居中盒子*/
margin: 0px auto;
/*水平居中文字*/
text-align: center;
}
当一个div元素设置margin:0 auto;时就会居中盒子,那我们知道margin:0 auto;表示上下外边距离为0,左右为auto的距离,那么auto是什么意思呢?
设置margin-left:auto;我们发现盒子尽可能大的右边有很大的距离,没有什么意义。当设置margin-right:auto;我们发现盒子尽可能大的左边有很大的距离。当两条语句并存的时候,我们发现盒子尽可能大的左右两边有很大的距离。此时我们就发现盒子居中了。
另外如何给盒子设置浮动,那么margin:0 auto失效。
使用margin:0 auto;注意点:
1.使用margin: 0 auto;水平居中盒子必须有width,要有明确width,文字水平居中使用text-align: center;
2.只有标准流下的盒子 才能使用margin:0 auto;
当一个盒子浮动了,固定定位,绝对定位(后面会讲),margin:0 auto; 不能用了
3.margin:0 auto;居中盒子。而不是居中文本,文字水平居中使用text-align: center;
另外大家一定要知道margin属性是描述兄弟盒子的关系,而padding描述的是父子盒子的关系
善于使用父亲的padding,而不是margin
如果让大家实现如图的效果,应该有不少的同学做不出来。
那么我们来看看这个案例,它的坑在哪里?
下面这个代码应该是大家都会去写的代码。
*{
padding: 0;
margin: 0;
}
.father{
width: 300px;
height: 300px;
background-color: blue;
}
.xiongda{
width: 100px;
height: 100px;
background-color: orange;
margin-top: 30px;
}
因为父亲没有border,那么儿子margin-top实际上踹的是“流” 踹的是行,所以父亲掉下来了,一旦给父亲一个border发现就好了。
那么问题来了,我们不可能在页面中无缘无故的去给盒子加一个border,所以此时的解决方案只有一种。就是使用父亲的padding。让子盒子挤下来。