1.在hello-koa这个目录下创建一个package.json,这个文件描述了我们的hello-koa工程会用到哪些包。完整的文件内容如下:

{
"name": "hello-koa2",
"version": "1.0.0",
"description": "Hello Koa 2 example with async",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"keywords": [
"koa",
"async"
],
"author": "Michael Liao",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/michaelliao/learn-javascript.git"
},
"dependencies": {
"koa": "2.0.0"
}
}

其中,dependencies描述了我们的工程依赖的包以及版本号。其他字段均用来描述项目信息,可任意填写。

C:\...\hello-koa> npm install

2.创建koa2工程

// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
const Koa = require('koa'); // 创建一个Koa对象表示web app本身:
const app = new Koa(); // 对于任何请求,app将调用该异步函数处理请求:
app.use(async (ctx, next) => {
await next();
ctx.response.type = 'text/html';
ctx.response.body = '<h1>Hello, koa2!</h1>';
}); // 在端口3000监听:
app.listen(3000);
console.log('app started at port 3000...');

还可以直接用命令node app.js在命令行启动程序,或者用npm start启动。npm start命令会让npm执行定义在package.json文件中的start对应命令:

"scripts": {
"start": "node app.js"
}

koa middleware

让我们再仔细看看koa的执行逻辑。核心代码是:

app.use(async (ctx, next) => {
await next();
ctx.response.type = 'text/html';
ctx.response.body = '<h1>Hello, koa2!</h1>';
});

每收到一个http请求,koa就会调用通过app.use()注册的async函数,并传入ctxnext参数。

我们可以对ctx操作,并设置返回内容。但是为什么要调用await next()

原因是koa把很多async函数组成一个处理链,每个async函数都可以做一些自己的事情,

然后用await next()来调用下一个async函数。我们把每个async函数称为middleware,这些middleware可以组合起来,完成很多有用的功能。

例如,可以用以下3个middleware组成处理链,依次打印日志,记录处理时间,输出HTML:

// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
const Koa = require('koa'); // 创建一个Koa对象表示web app本身:
const app = new Koa(); app.use(async (ctx, next) => {
console.log(`${ctx.request.method} ${ctx.request.url}`); // 打印URL
await next(); // 调用下一个middleware
}); app.use(async (ctx, next) => {
const start = new Date().getTime(); // 当前时间
await next(); // 调用下一个middleware
const ms = new Date().getTime() - start; // 耗费时间
console.log(`Time: ${ms}ms`); // 打印耗费时间
}); app.use(async (ctx, next) => {
console.log('我开始了')
await next(); // 当下面没有use 后将不执行
ctx.response.type = 'text/html';
ctx.response.body = '<h1>Hello, koa2!</h1>';
console.log('我结束了')
}); // 在端口3000监听:
app.listen(3000);
console.log('app started at port 3000...');

middleware的顺序很重要,也就是调用app.use()的顺序决定了middleware的顺序。

此外,如果一个middleware没有调用await next(),会怎么办?答案是后续的middleware将不再执行了。

这种情况也很常见,例如,一个检测用户权限的middleware可以决定是否继续处理请求,还是直接返回403错误:

app.use(async (ctx, next) => {
if (await checkUserPermission(ctx)) {
await next();
} else {
ctx.response.status = 403;
}
});

理解了middleware,我们就已经会用koa了!

最后注意ctx对象有一些简写的方法,例如ctx.url相当于ctx.request.urlctx.type相当于ctx.response.type

处理URL

正常情况下,我们应该对不同的URL调用不同的处理函数,这样才能返回不同的结果。例如像这样写:

// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
const Koa = require('koa'); // 创建一个Koa对象表示web app本身:
const app = new Koa(); app.use(async (ctx, next) => {
//ctx.request.path 判断访问路径
if (ctx.request.path === '/') {
ctx.response.body = 'index page';
//如果就有 就执行下一个use
} else {
await next();
}
}); app.use(async (ctx, next) => {
if (ctx.request.path === '/test') {
ctx.response.body = 'TEST page';
} else {
await next();
}
}); app.use(async (ctx, next) => {
if (ctx.request.path === '/error') {
ctx.response.body = 'ERROR page';
} else {
await next();
}
}); // 在端口3000监听:
app.listen(3000);
console.log('app started at port 3000...');

这么写是可以运行的,但是好像有点蠢。

应该有一个能集中处理URL的middleware,它根据不同的URL调用不同的处理函数,这样,我们才能专心为每个URL编写处理函数。

koa-router

为了处理URL,我们需要引入koa-router这个middleware,让它负责处理URL映射。

先在package.json中添加依赖项:

{
"name": "hello-koa2",
"version": "1.0.0",
"description": "Hello Koa 2 example with async",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"keywords": [
"koa",
"async"
],
"author": "Michael Liao",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/michaelliao/learn-javascript.git"
},
"dependencies": {
"koa": "2.0.0",
"koa-router": "7.0.0"
}
}

然后用npm install安装。

接下来,我们修改app.js,使用koa-router来处理URL:

// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
const Koa = require('koa'); // 注意require('koa-router')返回的是函数:koa-router的语句最后的()是函数调用:
const router = require('koa-router')(); // 创建一个Koa对象表示web app本身:
const app = new Koa(); //log request URL:
app.use(async(ctx,next)=> {
console.log(`Process ${ctx.request.method} ${ctx.request.url}.....`)
await next();
}) //add url-route; 添加访问路径
router.get(`/hello/:name`,async(ctx,next)=> {
var name = ctx.params.name
ctx.response.body = `<h1>Hello,${name}</h1>` }) //add url-route;添加访问路径
router.get('/', async (ctx, next) => {
ctx.response.body = '<h1>Index</h1>';
}); // add router middlware
app.use(router.routes()); // 在端口3000监听:
app.listen(3000);
console.log('app started at port 3000...');

注意导入koa-router的语句最后的()是函数调用:

const router = require('koa-router')();

相当于:

const fn_router = require('koa-router');
const router = fn_router();

然后,我们使用router.get('/path', async fn)来注册一个GET请求。可以在请求路径中使用带变量的/hello/:name,变量可以通过ctx.params.name访问。

再运行app.js,我们就可以测试不同的URL:

输入首页:http://localhost:3000/

处理post请求

router.get('/path', async fn)处理的是get请求。如果要处理post请求,可以用router.post('/path', async fn)

用post请求处理URL时,我们会遇到一个问题:post请求通常会发送一个表单,或者JSON,它作为request的body发送,但无论是Node.js提供的原始request对象,还是koa提供的request对象,都不提供解析request的body的功能!

所以,我们又需要引入另一个middleware来解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中。

koa-bodyparser就是用来干这个活的。

1. 在package.json中添加依赖项:

"koa-bodyparser": "3.2.0"

2.引入koa-bodyparser

const bodyParser = require('koa-bodyparser');

在合适的位置加上:

app.use(bodyParser());

由于middleware的顺序很重要,这个koa-bodyparser必须在router之前被注册到app对象上。

现在我们就可以处理post请求了。写一个简单的登录表单:

// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
const Koa = require('koa'); // 注意require('koa-router')返回的是函数:koa-router的语句最后的()是函数调用:
const router = require('koa-router')(); // 创建提供解析request的body的功能
const bodyParser = require('koa-bodyparser'); // 创建一个Koa对象表示web app本身:
const app = new Koa(); //log request URL:
app.use(async(ctx,next)=> {
console.log(`Process ${ctx.request.method} ${ctx.request.url}.....`)
await next();
}) //get 请求返回一个body html文本
router.get('/', async (ctx, next) => {
// 响应html文本
ctx.response.body = `<h1>Index</h1>
<form action="/signin" method="post">
<p>Name: <input name="name" value="koa"></p>
<p>Password: <input name="password" type="password"></p>
<p><input type="submit" value="Submit"></p>
</form>`;
}); //处理用户提交过来的 post请求
router.post('/signin', async (ctx, next) => {
// 获取用户提交上来的值
var
name = ctx.request.body.name || '',
password = ctx.request.body.password || '';
console.log(`signin with name: ${name}, password: ${password}`); // 对用户提交上来的值进行判断
if (name === 'koa' && password === '12345') {
//如果成功 返回登录成功
ctx.response.body = `<h1>Welcome, ${name}!</h1>`;
} else {
// 如果登录失败 返回登录失败,然后尝试 再次登录
ctx.response.body = `<h1>Login failed!</h1>
<p><a href="/">Try again</a></p>`;
}
}); // add middlware 这个要放前面
app.use(bodyParser()); // add router middlware
app.use(router.routes()); // 在端口3000监听:
app.listen(3000);
console.log('app started at port 3000...');

注意到我们用var name = ctx.request.body.name || ''拿到表单的name字段,如果该字段不存在,默认值设置为''

类似的,put、delete、head请求也可以由router处理。

重构

现在,我们已经可以处理不同的URL了,但是看看app.js,总觉得还是有点不对劲。

所有的URL处理函数都放到app.js里显得很乱,而且,每加一个URL,就需要修改app.js。随着URL越来越多,app.js就会越来越长。

如果能把URL处理函数集中到某个js文件,或者某几个js文件中就好了,然后让app.js自动导入所有处理URL的函数。这样,代码一分离,逻辑就显得清楚了。最好是这样:

url2-koa/
|
+- .vscode/
| |
| +- launch.json <-- VSCode 配置文件
|
+- controllers/
| |
| +- login.js <-- 处理login相关URL
| |
| +- users.js <-- 处理用户管理相关URL
|
+- app.js <-- 使用koa的js
|
+- package.json <-- 项目描述文件
|
+- node_modules/ <-- npm安装的所有依赖包

于是我们把url-koa复制一份,重命名为url2-koa,准备重构这个项目。

我们先在controllers目录下编写index.js

var fn_index = async (ctx, next) => {
ctx.response.body = `<h1>Index</h1>
<form action="/signin" method="post">
<p>Name: <input name="name" value="koa"></p>
<p>Password: <input name="password" type="password"></p>
<p><input type="submit" value="Submit"></p>
</form>`;
}; var fn_signin = async (ctx, next) => {
var
name = ctx.request.body.name || '',
password = ctx.request.body.password || '';
console.log(`signin with name: ${name}, password: ${password}`);
if (name === 'koa' && password === '12345') {
ctx.response.body = `<h1>Welcome, ${name}!</h1>`;
} else {
ctx.response.body = `<h1>Login failed!</h1>
<p><a href="/">Try again</a></p>`;
}
}; module.exports = {
'GET /': fn_index,
'POST /signin': fn_signin
};

这个index.js通过module.exports把两个URL处理函数暴露出来。

类似的,hello.js把一个URL处理函数暴露出来:

var fn_hello = async (ctx, next) => {
var name = ctx.params.name;
ctx.response.body = `<h1>Hello, ${name}!</h1>`;
}; module.exports = {
'GET /hello/:name': fn_hello
};

现在,我们修改app.js,让它自动扫描controllers目录,找到所有js文件,导入,然后注册每个URL:

路径分发url

// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
const Koa = require('koa'); // 创建一个Koa对象表示web app本身:
const app = new Koa(); // 注意require('koa-router')返回的是函数:koa-router的语句最后的()是函数调用:
const router = require('koa-router')(); // 创建提供解析request的body的功能
const bodyParser = require('koa-bodyparser'); // 先导入fs模块,然后用readdirSync列出文件
var fs = require('fs'); // 这里可以用sync是因为启动时只运行一次,不存在性能问题:
// __dirname === 当前根目录名
// fs.readdirSync 读出文件夹中的文件
var files = fs.readdirSync(__dirname + '/controllers'); // 过滤出.js文件: [ 'hello.js', 'index.js', 'login.js' ]
var js_files = files.filter((f)=>{
return f.endsWith('.js');
}); // 处理每个js文件:
for (var f of js_files) {
// console.log(`process controller: ${f}...`);
// 导入js文件: 当前目录controllers+当前循环文件 { 'GET /hello/:name': [AsyncFunction: fn_hello] }
let mapping = require(__dirname + '/controllers/' + f) for (var url in mapping) {//GET /hello/:name GET / POST /signin
// 如果url以"GET xxx"开头:
if (url.startsWith('GET ')) {
var path = url.substring(4); // 去除字符串前面的4个字符。 GET /(这四个)
//取到路径 path == /hello/:name, mapping[url] == [AsyncFunction: fn_hello]
router.get(path, mapping[url])
console.log(`register URL mapping: GET ${path}`);
} else if (url.startsWith(`POST`)){ //处理post请求
// 去除前面的post加一个空格 POST
var path = url.substring(5);
router.post(path,mapping[url]);
console.log(`register URL mapping: POST ${path}`)
} else {
// 无效的的url
console.log(`invalid URL: ${url}`)
}
}
} // add middlware 这个要放前面
app.use(bodyParser()); // add router middlware
app.use(router.routes()); app.listen(3000)
console.log('app started at port 3000...');

处理请求页面

var fn_login = async (ctx, next)=> {
ctx.response.body = `
<form action="/mark" method="POST">
<input type="text" name="use" value="riven">
<input type="password" name="pwd">
<input type="submit" >
</form>
`
} var fn_login_post = async (ctx,next) => {
var name = ctx.request.body.use || '';
var password = ctx.request.body.pwd ||'';
console.log(`signin with name: ${name}, password: ${password}`);
if (name === 'riven' && password ==='123456') {
ctx.response.body = `<h1> Welcome,${name}!<h1>`
} else {
ctx.response.body = `<h1>Login failed!<h1>
<p><a href='/'>Try agein</a></p>
`
}
} module.exports = {
'GET /': fn_login,
'POST /mark': fn_login_post
};

如果上面的大段代码看起来还是有点费劲,那就把它拆成更小单元的函数:(简化函数版)

处理url

// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
const Koa = require('koa'); // 创建一个Koa对象表示web app本身:
const app = new Koa(); // 注意require('koa-router')返回的是函数:koa-router的语句最后的()是函数调用:
const router = require('koa-router')(); // 创建提供解析request的body的功能
const bodyParser = require('koa-bodyparser'); // 先导入fs模块,然后用readdirSync列出文件
var fs = require('fs'); // 读取文件 处理路径
function fileControllers(router){
// 这里可以用sync是因为启动时只运行一次,不存在性能问题:
// __dirname === 当前根目录名
// fs.readdirSync 读出文件夹中的文件
var files = fs.readdirSync(__dirname + '/controllers'); // 过滤出.js文件: [ 'hello.js', 'index.js', 'login.js' ]
var js_files = files.filter((f)=>{
return f.endsWith('.js');
}); // 处理每个js文件:
for (var f of js_files) {
// console.log(`process controller: ${f}...`);
// 导入js文件: 当前目录controllers+当前循环文件 { 'GET /hello/:name': [AsyncFunction: fn_hello] }
let mapping = require(__dirname + '/controllers/' + f)
requestControllers(router,mapping) }
} // 处理request请求
function requestControllers(router, mapping){
for (var url in mapping) {//GET /hello/:name GET / POST /signin
// 如果url以"GET xxx"开头:
if (url.startsWith('GET ')) {
var path = url.substring(4); // 去除字符串前面的4个字符。 GET /(这四个)
//取到路径 path == /hello/:name, mapping[url] == [AsyncFunction: fn_hello]
router.get(path, mapping[url])
console.log(`register URL mapping: GET ${path}`);
} else if (url.startsWith(`POST`)){ //处理post请求
// 去除前面的post加一个空格 POST
var path = url.substring(5);
router.post(path,mapping[url]);
console.log(`register URL mapping: POST ${path}`)
} else {
// 无效的的url
console.log(`invalid URL: ${url}`)
}
}
} fileControllers(router) // add middlware 这个要放前面
app.use(bodyParser()); // add router middlware
app.use(router.routes()); app.listen(3000)
console.log('app started at port 3000...');

页面

var fn_login = async (ctx, next)=> {
ctx.response.body = `
<form action="/mark" method="POST">
<input type="text" name="use" value="riven">
<input type="password" name="pwd">
<input type="submit" >
</form>
`
} var fn_login_post = async (ctx,next) => {
var name = ctx.request.body.use || '';
var password = ctx.request.body.pwd ||'';
console.log(`signin with name: ${name}, password: ${password}`);
if (name === 'riven' && password ==='123456') {
ctx.response.body = `<h1> Welcome,${name}!<h1>`
} else {
ctx.response.body = `<h1>Login failed!<h1>
<p><a href='/'>Try agein</a></p>
`
}
} module.exports = {
'GET /': fn_login,
'POST /mark': fn_login_post
};

Controller Middleware

最后,我们把扫描controllers目录和创建router的代码从app.js中提取出来,作为一个简单的middleware使用,命名为controller.js

这样一来,我们在app.js的代码又简化了:

...

// 导入controller middleware:
const controller = require('./controller'); ... // 使用middleware:
app.use(controller());

经过重新整理后的工程url2-koa目前具备非常好的模块化,所有处理URL的函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,app.js保持不变。

login.js 页面

var fn_login = async (ctx, next)=> {
ctx.response.body = `
<form action="/mark" method="POST">
<input type="text" name="use" value="riven">
<input type="password" name="pwd">
<input type="submit" >
</form>
`
} var fn_login_post = async (ctx,next) => {
var name = ctx.request.body.use || '';
var password = ctx.request.body.pwd ||'';
console.log(`signin with name: ${name}, password: ${password}`);
if (name === 'riven' && password ==='123456') {
ctx.response.body = `<h1> Welcome,${name}!<h1>`
} else {
ctx.response.body = `<h1>Login failed!<h1>
<p><a href='/'>Try agein</a></p>
`
}
} module.exports = {
'GET /': fn_login,
'POST /mark': fn_login_post
};

controllers.js

// 先导入fs模块,然后用readdirSync列出文件
var fs = require('fs'); // 读取文件 处理路径
function fileControllers(router,dir){
// 这里可以用sync是因为启动时只运行一次,不存在性能问题:
// __dirname === 当前根目录名
// fs.readdirSync 读出文件夹中的文件
var files = fs.readdirSync(__dirname + `/${dir}`); // 过滤出.js文件: [ 'hello.js', 'index.js', 'login.js' ]
var js_files = files.filter((f)=>{
return f.endsWith('.js');
}); // 处理每个js文件:
for (var f of js_files) {
// console.log(`process controller: ${f}...`);
// 导入js文件: 当前目录controllers+当前循环文件 { 'GET /hello/:name': [AsyncFunction: fn_hello] }
let mapping = require(__dirname + `/${dir}/` + f)
requestControllers(router,mapping) }
} // 处理request请求
function requestControllers(router, mapping){
for (var url in mapping) {//GET /hello/:name GET / POST /signin
// 如果url以"GET xxx"开头:
if (url.startsWith('GET ')) {
var path = url.substring(4); // 去除字符串前面的4个字符。 GET /(这四个)
//取到路径 path == /hello/:name, mapping[url] == [AsyncFunction: fn_hello]
router.get(path, mapping[url])
console.log(`register URL mapping: GET ${path}`);
} else if (url.startsWith(`POST`)){ //处理post请求
// 去除前面的post加一个空格 POST
var path = url.substring(5);
router.post(path,mapping[url]);
console.log(`register URL mapping: POST ${path}`)
} else {
// 无效的的url
console.log(`invalid URL: ${url}`)
}
}
} module.exports = function (dir){
let
controllers_dir = dir || `controllers` // 如果不传参数,扫描目录默认为'controllers'
// 注意require('koa-router')返回的是函数:koa-router的语句最后的()是函数调用:
const router = require('koa-router')();
fileControllers(router, controllers_dir)
return router.routes() };

app.js

// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
const Koa = require('koa'); // 创建一个Koa对象表示web app本身:
const app = new Koa(); // 创建提供解析request的body的功能
const bodyParser = require('koa-bodyparser'); // 导入controller middleware:
const controller = require('./controller') // add middlware 这个要放前面
app.use(bodyParser()); // 使用middleware: app.use(router.routes())
app.use(controller()); app.listen(3000)
console.log('app started at port 3000...');

经过重新整理后的工程url2-koa目前具备非常好的模块化,所有处理URL的函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,app.js保持不变。

koa web框架入门的更多相关文章

  1. 教程:Visual Studio 中的 Django Web 框架入门

    教程:Visual Studio 中的 Django Web 框架入门 Django 是高级 Python 框架,用于快速.安全及可扩展的 Web 开发. 本教程将在 Visual Studio 提供 ...

  2. Python Flask Web 框架入门

    Python Flask 目录 本文主要借鉴 letiantian 的文章 http://www.letiantian.me/learn-flask/ 一.简介 二.安装 三.初始化Flask 四.获 ...

  3. 比我的脸还干的gan货——Python Flask Web 框架入门

    Flask是一个轻量级的基于Python的web框架. 本文适合有一定HTML.Python.网络基础的同学阅读. 1. 简介 这份文档中的代码使用 Python 3 运行.是的,所以读者需要自己在电 ...

  4. Django Web框架入门

    一.Django介绍 Django是一个开放源代码的Web应用框架,由Python写成,采用了MVC的框架模式.Django的主要目的是简便.快速的开发数据库驱动的网站.它强调代码复用,多个组件可以很 ...

  5. Express NodeJs Web框架 入门笔记

    Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具. 使用 Express 可以快速地搭建一个完整功能的网 ...

  6. 最快的 Python Web 框架入门

    速度比较 框架 实现基础 每秒请求数 平均时间 Sanic Python 3.5 + uvloop 30,601 3.23ms Wheezy gunicorn + meinheld 20,244 4. ...

  7. Koa – 更加强大的下一代 Node.js Web 框架

    Koa 是 Express 的开发团队设计的下一代 Web 框架,其目的是为 Web 应用程序提供更小,更具表现力,更坚实的基础.Koa 没有核捆绑任何中间件,并提供了一​​套优雅的方法,使服务器端开 ...

  8. koa : Express出品的下一代基于Node.js的web框架

    https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001434501579966a ...

  9. 新一代web框架Koa源码学习

    此文已由作者张佃鹏授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. Koa 就是一种简单好用的 Web 框架.它的特点是优雅.简洁.表达力强.自由度高.本身代码只有1000多行 ...

  10. 参考KOA,5步手写一款粗糙的web框架

    我经常在网上看到类似于KOA VS express的文章,大家都在讨论哪一个好,哪一个更好.作为小白,我真心看不出他两who更胜一筹.我只知道,我只会跟着官方文档的start做一个DEMO,然后我就会 ...

随机推荐

  1. The attempt was made from the following location: com.ruoyi.framework.config.ResourcesConfig.corsFilter(ResourcesConfig.java:57)

    报错信息: 8:42:12.529 [restartedMain] ERROR o.s.b.w.e.t.TomcatStarter - [onStartup,61] - Error starting ...

  2. kubernetes之包管理器Helm

    安装helm 安装helm客户端 [machangwei@mcwk8s-master ~]$ curl https://raw.githubusercontent.com/kubernetes/hel ...

  3. Three加载3D模型贴图

    Three加载3D模型贴图 准备阶段 3D模型 three 库文件 纹理图片 相关资料 官方开发文档: https://threejs.org/docs 官网编辑3D模型:https://threej ...

  4. linux文件权限管理:文件权限类型,文件权限影响,设定文件权限,取消文件权限

    目录 一.关于文件权限 二.查看文件权限 三.linux下常见文件类型 四.linux下常见的文件权限 五.权限对文件和目录的影响 六.文件的用户分类 七.更改文件的属主和属组 八.一个文件取消所有权 ...

  5. 5款超好用的AI换脸软件,一键视频直播换脸(附下载链接)

    随着AIGC的火爆,AI换脸技术也被广泛应用于娱乐.广告.电影制作等领域,本期文章系统介绍了市面上超火的5款AI软件 换脸整合包收录了全部5款AI工具,请按照需要选择下载: 百度网盘:https:// ...

  6. C# 炸弹人 winform版

    实现这个游戏的基本功能包含几个对象:玩家,怪物,墙砖,炸弹,通关的门.玩家通过上下左右方向键移动,放置炸弹,被怪物杀死,被炸弹炸死.怪物随机方向移动,能杀死玩家.炸弹有爆炸功能,炸弹的火花长度.通过的 ...

  7. 必应每日一图url(可直接使用)

    必应每日一图url 首先放出地址,后面是一堆心路历程(一堆废话),只为链接的可以不用看 https://baotangguo.cn:8081/ 最初 ​ 博客园装饰的时候(虽然是抄的),想着上面背景图 ...

  8. c#WinFrom自定义图表仪表控件-频谱

    这是为客户定制的一个频谱图表控件,先看下成品效果,gif较大,略等片刻 开发步骤分析: 1.界面有多个间距不等的线分割的区域,每个区域的值范围不同,我们就需要把每个区域定义出来,方便我们操作的时候来计 ...

  9. node child_process模块exec

    child_process是Node.js自带的核心模块之一,无需额外安装即可使用. child_process模块提供了创建子进程的功能,可以在Node.js中执行外部命令.脚本文件等,并与其进行交 ...

  10. vue过滤器 - filters

    在数据被渲染之前,可以对其进行进一步处理,比如将字符截取或者将小写统一转换为大写等等,过滤器本身就是一个方法. 过滤器可以定义全局或局部 # 全局 // 回调函数中的参数1永久是绑定的数据 Vue.f ...