diff --git "a/\351\203\221\344\274\237\346\235\260/20260316-\345\237\272\346\234\254\345\206\205\347\275\256\346\250\241\345\235\227.md" "b/\351\203\221\344\274\237\346\235\260/20260316-\345\237\272\346\234\254\345\206\205\347\275\256\346\250\241\345\235\227.md" new file mode 100644 index 0000000000000000000000000000000000000000..e3b3ec269d48d82f25232d658e83d00a7f0ad0f9 --- /dev/null +++ "b/\351\203\221\344\274\237\346\235\260/20260316-\345\237\272\346\234\254\345\206\205\347\275\256\346\250\241\345\235\227.md" @@ -0,0 +1,241 @@ +# 笔记 + +## 基本内置模块 +### 内置模块的特点 +1. 无需安装 +2. 性能优化 +3. 功能强大 +4. 稳定可靠 +### 四大基本内置模块 +1. console模块 + - console.log(); + - console.info() 信息; + - console.warn() 警告; + - console.error() 错误; + - 高级用法 + - console.table([]) 表格输出 + - console.count() 计数功能 + - ```js + console.time() //计时 + for(let i=0;i<10000;i++){} + console.timeEnd(); + ``` + - console.assert(1 === 1, '这不会显示'); + - ```js + function fn1() { fn2(); } + function fn2() { console.trace('调用追踪'); } + fn1(); + ``` +2. process模块 + - 进程信息 + - node.js版本信息 + - process.version // v24.x.x + - process.version.node // 24.x.x + - process.version.v8 // x.x.x. + - 平台信息 + - process.platform // win32/linux等 + - process.arch //x64/arm64 + - 当前工作目录 + - process.cwd() //返回当前工作目录 + - 环境变量 + - process.env.USER //用户名 + - process.env.NODE_ENV //环境变量 + - 以上要跟console.log输出 + - 进程控制 + - 退出程序 + - process.exit(0); //正常退出 + - process.exit(1); //异常退出 + - 监听退出信号 + - ```js + process.on('exit', (code) => { + console.log('程序即将退出,退出码:' + code); + }); + process.on('SIGINT', () => { + console.log('收到Ctrl+C信号'); + process.exit(0); + }); + ``` + - 监听未捕获的异常 + - ```js + process.on('uncaughtException', (err) => { + console.error('未捕获的异常:', err); + process.exit(1); + }); + ``` + - 命令行 + - process.argv +3. Buffer模块 + - 用于处理二进制数据 +4. path模块 + - 获取目录 + - _dirname 当前文件所在目录 + - 获取文件名 + - _filename 当前文件完整路径 + - 拼接路径 + - let 命名 =path.join(获取目录,'src','index.js') + - console.log(命名); // .../src/index.js + - 解析为绝对路径 + - path.resolve() + - const absPath = path.resolve('index.js'); + - console.log(absPath); // C:/Users/.../index.js + - 获取文件名 + - basename(文件); + - basename(文件,'.js') + - 获取扩展名 + - extname(文件) + - 获取目录 + - dirname(文件) // /Users/zhangsan/project/src + - 解析路径信息 + - parse(文件) + +5. import 名 from 模块 就可以简写 + + +# 练习 +## 选择题 +```bash +1. 下面哪个方法可以获取Node.js的版本号? +B. process.version + +2. Buffer.alloc(10)创建的Buffer,长度是多少字节? +A. 10 + + +3. "你好"的Buffer长度是? +C. 6 + +4. __dirname表示什么? + +B. 当前文件所在目录 + +console.time()和console.timeEnd()的作用是? + +A. 计时 + +``` +## 简答题 +```bash +1.请解释console.log和console.error的区别。 + 输出和输出异常 +process.argv返回的数组包含哪些内容?请举例说明。 + 第一个元素是 Node.js 可执行文件的绝对路径。 + 第二个元素是当前执行的 JavaScript 文件的路径。 + 从第三个元素开始,是用户传入的其他参数 + +2.Buffer和普通数组有什么区别? + Buffer:存储原始二进制数据,每个元素是 0–255 的整数 + 长度固定 + 通过索引读写,得到的是字节值 + 普通数组: 可以存储任意类型的 JavaScript 值(数字、字符串、对象等) + 长度可变 + 索引读写,得到存储元素 +3.__dirname和process.cwd()有什么区别? + 一个是当前执行模块所在的目录的绝对路径 + 一个是当前工作目录 +4.path.join()和path.resolve()有什么区别? +将多个路径片段拼接成一个路径 +将多个路径片段解析为一个绝对路径 +``` +## 操作题 +1. **信息展示**:创建一个程序,输出当前系统的所有环境变量。 + ```js + import {log} from 'console'; + //信息展示:创建一个程序,输出当前系统的所有环境变量。 + log("=====环境变量=====") + log('NODE_ENV',process.env.NODE_ENV||'未设置') + log('HOME',process.env.HOME|| process.env.USERPROFILE) + ``` + - ![](https://gitee.com/fish-ovo_1_0/pig-go/raw/master/image/20260322155236311.png) +2. **参数解析**:创建一个计算器程序,支持: + - `node calc.js add 5 3` → 输出 8 + - `node calc.js sub 5 3` → 输出 2 + - `node calc.js mul 5 3` → 输出 15 + ```js + import {log} from 'console'; + + let opt =process.argv[2] + let one =process.argv[3]*1 + let two =process.argv[4]*1 + + if(opt=='add'){ + log(one+'+'+two+'='+(one+two)) + }else if(opt=='sub'){ + log(one+'-'+two+'='+(one-two)) + }else if(opt=='mul'){ + log(one+'x'+two+'='+(one*two)) + } + ``` + - ![](https://gitee.com/fish-ovo_1_0/pig-go/raw/master/image/20260322155326899.png) +3. **Buffer操作**:创建一个程序,将中文字符串转为Buffer,再转回字符串,验证是否一致。 + ```js + import {log} from 'console'; + //创建一个程序,将中文字符串转为Buffer,再转回字符串 + + let par=process.argv[2]; + + let buf=Buffer.from('牛逼'); + + + log('原数据:'+par); + log(buf) + log('字符串:'+buf.toString()) + ``` + - ![](https://gitee.com/fish-ovo_1_0/pig-go/raw/master/image/20260322155445329.png) +4. **路径处理**:创建一个程序,接收文件路径,输出文件名、扩展名、所在目录。 +```js +import {log} from 'console' +import path from 'path' +import fs from 'fs' +//接收文件路径,输出文件名、扩展名、所在目录 +let filepath=process.argv[2] +if(!filepath){ + log('请输入文件路径') + process.exit(1) +} +if (!fs.existsSync(filepath)) { + log(`错误:文件不存在 -> ${filepath}`); + process.exit(1); // 退出程序,终止后续执行 +} +log('文件名:'+path.basename(filepath)) +log('扩展名:'+path.extname(filepath)) +let abs=path.resolve(filepath) +log('所在目录:'+path.dirname(abs)) +``` +- ![](https://gitee.com/fish-ovo_1_0/pig-go/raw/master/image/20260322155531341.png) +5. **综合应用**:创建一个CLI工具,接受命令行参数,实现简单的待办列表功能(添加、列出、删除)。 +```js +import fs from 'fs' + +if(opt=='list'){ + console.log('-------待办事项[√]-------'); + console.log(read()); +}else if(opt == 'add'){ + console.log('添加'); + let arr=read(); + arr.push({'title':par,'isDone':false}); + white(arr); + console.log(read()); +}else if(opt=='del'){ + console.log("删除成功"); + //把字符串转换为数字 + let delArr=read(); + delArr.splice(idx-1,1); + white(delArr); +} + +function read(filePath){ + filePath=filePath ||'todo.json' + if(fs.existsSync(filePath)){ + let data=fs.readFileSync(filePath,'utf-8') + return JSON.parse(data)||[] + } + return [] +} +function white(fileConent,filePath){ + filePath=filePath ||'todo.json' + let jStr=JSON.stringify(fileConent) + fs.writeFileSync(filePath,jStr) +} + +``` +- ![](https://gitee.com/fish-ovo_1_0/pig-go/raw/master/image/20260322155941789.png) \ No newline at end of file diff --git "a/\351\203\221\344\274\237\346\235\260/20260318-\346\226\207\344\273\266\347\263\273\347\273\237\346\250\241\345\235\227.md" "b/\351\203\221\344\274\237\346\235\260/20260318-\346\226\207\344\273\266\347\263\273\347\273\237\346\250\241\345\235\227.md" new file mode 100644 index 0000000000000000000000000000000000000000..ffd92dba5f279ae9bfac5486774f6db231d1dc8c --- /dev/null +++ "b/\351\203\221\344\274\237\346\235\260/20260318-\346\226\207\344\273\266\347\263\273\347\273\237\346\250\241\345\235\227.md" @@ -0,0 +1,136 @@ +# 笔记 + +## fs模块的特点 +1. 两种方式 + - 同步(带Sync) + - 异步 (回调、Promise) +2. 流式处理 支持大文件流式读写 +3. 支持设置文件权限 + + +## 读取文件 +1. 同步读取 + - let content = fs.readFileSync('./data.txt', 'utf8'); +2. 异步读取(回调方式) + - ```js + fs.readFile('./data.txt', 'utf8', (err, data) => { + if (err) { + console.error('读取失败:', err); + return; + } + console.log('文件内容:', data); + }); + ``` +3. 异步读取(Promise) + - ```js + async function readFile() { + try { + const content = await fs.readFile('./data.txt', 'utf8'); + console.log(content); + } catch (err) { + console.error('读取失败:', err); + } + } + ``` +## 写入文件 +1. 同步写入 + - fs.writeFileSync('./data.txt', 'Hello World'); + - 追加写入 + - fs.appendFileSync('./log.txt', '新内容\n'); +2. 异步写入 + - ```js + fs.writeFile('./data.txt', 'Hello World', 'utf8', (err) => { + if (err) throw err; + console.log('写入成功'); + }); + ``` + - 异步追加 + - ```js + fs.appendFile('./log.txt', '新内容\n', (err) => { + if (err) throw err; + console.log('追加成功'); + }); + ``` +3. 异步Promise方式 + - ```js + async function writeData() { + await fs.writeFile('./data.txt', 'Hello World', 'utf8'); + console.log('写入成功'); + } + ``` + +## 文件操作 +1. 检查文件是否存在 + - existsSync + - 返回 true 或者 false +2. 获取文件状态 + - statSync + - 以下前面都是stats. + - 是文件 + - isFlie + - 是目录 + - isDirectory + - 文件大小 + - size + - 创建时间 + - birthtime + - 修改时间 + - mtime +3. 删除文件 + - unlinkSync 同步 + - 异步 + - ```js + fs.unlink('./temp.txt', (err) => { + if (err) throw err; + console.log('删除成功'); + }); + ``` + - 异步Promise + - await fs.unlink('') +4. 复制文件 + - ```js + function copyFile(src, dest) { + const data = fs.readFileSync(src); + fs.writeFileSync(dest, data); + } + copyFile('./source.txt', './dest.txt'); + ``` +5. 重命名 + - fs.renameSync('./old.txt', './new.txt'); +6. 移动(跨目录重命名) + - fs.renameSync('./file.txt', './backup/file.txt'); +## 目录操作 +1. 创建目录 + - 创建单个目录(同步) + - ```js + fs.mkdirSync('./backup'); + ``` + - 创建多级目录(同步) + - ```js + fs.mkdirSync('./backup/data/images', { recursive: true }); + ``` + - 异步创建 + - ```js + fs.mkdir('./backup', { recursive: true }, (err) => { + if (err) throw err; + console.log('创建成功'); + }); + ``` +2. 读取目录 + - 读取目录内容(同步) + - ```js + const files = fs.readdirSync('./src'); + console.log(files); // ['index.js', 'utils.js', 'data'] + ``` + - 读取详细信息 + - ```js + const items = fs.readdirSync('./src', { withFileTypes: true }); + for (const item of items) { + console.log(`${item.name} - ${item.isDirectory() ? '目录' : '文件'}`); + } + ``` +3. 删除目录 + - 删除空目录 (同步) + - fs.rmdirSync('./empty-dir'); + - 删除目录及内容(同步) + - fs.rmSync('./backup', { recursive: true, force: true }); diff --git "a/\351\203\221\344\274\237\346\235\260/20260319-\346\226\207\344\273\266\347\263\273\347\273\237\346\250\241\345\235\227\344\275\234\344\270\232.md" "b/\351\203\221\344\274\237\346\235\260/20260319-\346\226\207\344\273\266\347\263\273\347\273\237\346\250\241\345\235\227\344\275\234\344\270\232.md" new file mode 100644 index 0000000000000000000000000000000000000000..c4c4f975dbbef4a9df9b6eded87c897e7d3bc18e --- /dev/null +++ "b/\351\203\221\344\274\237\346\235\260/20260319-\346\226\207\344\273\266\347\263\273\347\273\237\346\250\241\345\235\227\344\275\234\344\270\232.md" @@ -0,0 +1,230 @@ +# 练习 +## 选择题 +```bash +下面哪个方法可以检查文件是否存在? + B. fs.existsSync() + +写入文件使用哪个方法? + B. fs.writeFileSync() + +appendFileSync和writeFileSync的区别是? + B. append是追加,write是覆盖 + +如何创建一个多级目录? + B. fs.mkdirSync('./a/b/c', { recursive: true }) + +下面哪个方法可以获取文件的详细信息(大小、时间等)? + C. fs.statSync() + +``` +## 简答题 +```bash +请解释同步方法和异步方法的区别。 + 同:代码按顺序执行,直到操作完成才继续 + 异:非阻塞,调用后立即返回 + +fs.readFile和fs.readFileSync有什么区别? + 一个异步 + 一个同步 + +如何实现文件的追加写入? + 使用 fs.appendFile 或 fs.appendFileSync 方法直接追加内容 + +什么是流式处理?它适合什么场景? + 将数据分成小块逐步处理 + +如何监听文件的变化? +使用 fs.watch 或者 fs.watchFile +``` +## 操作题 +1. 文件读取器:创建一个程序,读取指定文件的内容并输出到控制台。 +```js +import { log } from 'console' +import fs from 'fs' + +let filePath=process.argv[2] +if(!filePath){ + log('你文件路径都没写,想怎?') +} + +fs.readFile(filePath,'utf-8',(err,data)=>{ + if(err){ + log('读取失败,有这文件吗') + return + } + log(data) +}) +``` +- ![](https://gitee.com/fish-ovo_1_0/pig-go/raw/master/image/20260322163307773.png) + +2. 文件写入器:创建一个程序,接收命令行输入的内容,写入到文件中。 +```js +import fs from 'fs' + +let filePath=process.argv[2] +let content=process.argv[3] + + +if(!filePath){ + log('你文件路径都没写,想怎?') +} + +fs.appendFile(filePath, content+'\n', 'utf8', (err) => { + if (err) { + console.error('写入文件失败:', err.message); + return; + } + console.log('文件写入成功'); +}); +``` +- ![](https://gitee.com/fish-ovo_1_0/pig-go/raw/master/image/20260322164444872.png) + +3. 目录复制:实现一个函数,递归复制整个目录。 +```js +import fs from 'fs'; +import path from 'path'; + +let src = process.argv[2]; +let dest = process.argv[3]; + +function copyFile(src, dest) { + // 确保目标目录存在 + const destDir = path.dirname(dest); + if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, { recursive: true }); + } + let data = fs.readFileSync(src); + fs.writeFileSync(dest, data); +} + +function copyDir(src, dest) { + // 确保目标目录存在 + if (!fs.existsSync(dest)) { + fs.mkdirSync(dest, { recursive: true }); + } + + // 读取源目录内容 + const entries = fs.readdirSync(src, { withFileTypes: true }); + + for (const entry of entries) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + + if (entry.isDirectory()) { + // 递归复制子目录 + copyDir(srcPath, destPath); + } else { + // 复制文件 + copyFile(srcPath, destPath); + } + } +} + +// 判断源路径是否存在 +if (!fs.existsSync(src)) { + console.log("源路径不存在"); + process.exit(1); +} + +// 获取源路径 +const srcStat = fs.statSync(src); + +if (srcStat.isDirectory()) { + // 如果是目录,调用递归复制目录函数 + copyDir(src, dest); + console.log(`目录复制完成:${src} -> ${dest}`); +} else { + // 如果是文件,直接复制单个文件 + copyFile(src, dest); + console.log(`文件复制完成:${src} -> ${dest}`); +} +``` +- ![](https://gitee.com/fish-ovo_1_0/pig-go/raw/master/image/20260322164906438.png) + +4. 文件搜索:创建一个文件搜索工具,查找指定目录下所有特定类型的文件(如.js文件)。 +```js +// 文件搜索:创建一个文件搜索工具, +// 查找指定目录下所有特定类型的文件(如.js文件)。 + +import { log } from 'console'; +import fs from 'fs' +import path from 'path' +//参数获取 +let mulu=process.argv[2] +let type=process.argv[3] +//校验参数 +if(!mulu || !type){ + log('用法: node app.js <目录> 《扩展名》') + process.exit(1) +} + + +let stats = fs.statSync(mulu); +if (stats.isDirectory()) { + console.log('目录'); + //得到目录下的所有文件 + let item = fs.readdirSync(mulu); + log(item); + log('上面是这个目录下的所有文件') + //获取扩展名 + log(type+'文件有以下') + for(let i=0;i{ + // req: 请求对象 + // res: 响应对象 + //返回响应 + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('Hello World!'); + }) + // 监听端口 + server.listen(3000, () => { + console.log('服务器运行在 http://localhost:3000'); + }); + ``` +### 请求对象(req) +1. 请求方法 + - req.method // GET,POST,PUT,DELETE +2. 请求URL + - req.url (/ , /about ,/api/user) +3. 请求头 + - 'User-Agent:', req.headers['user-agent'] +4. URL路径(不含查询字符串) + ```js + const urlObj = new URL(req.url, `http://${req.headers.host}`); + console.log('路径:', urlObj.pathname); + ``` +5. 查询参数 + ```js + urlObj.searchParams.get('name') + res.end('ok') + ``` +### 响应对象(res) +1. 设置状态码 + - res.statusCode = + - 200 成功 + - 404 未找到 + - 500 服务器错误 +2. 设置响应头 + - res.setHeader('Content-Type', 'text/html; charset=utf-8') + - res.setHeader('Content-Type', 'application/json') +3. 写入响应头 + ```js + res.writeHead(200, { + 'Content-Type': 'text/html; charset=utf-8', + 'Access-Control-Allow-Origin': '*' + }); + ``` +4. 发送响应体 + - res.end('') +5. 或者发送JSON + - res.end(JSON.stringify({ name: '张三', age: 20 })) +## 处理不同请求方法 + +# 练习 +## 6.1 选择题 +```bash +1. 创建一个HTTP服务器需要使用哪个模块? + - B. http + +2. 获取请求的方法(GET、POST)应该访问哪个属性? + - B. req.method + +3. 设置响应状态码使用哪个属性? + - B. res.statusCode + +4. 返回JSON数据需要设置什么响应头? + - B. 'Content-Type': 'application/json' + +5. 使用哪个方法发送响应? + - C. res.end() +``` +## 6.2 简答题 +```bash +1. 请解释req和res分别代表什么? + 一个是请求对象 + 一个是响应对象 + +2. http.createServer的回调函数接收哪些参数? + req和res + +3. 如何获取URL中的查询参数? + url.parse + +4. res.writeHead和res.setHeader有什么区别? + res.setHeader(name, value):单独设置一个响应头 + res.writeHead(statusCode[, statusMessage][, headers]):同时设置 HTTP 状态码、状态消息和多个响应头 +5. http.get和http.request有什么区别? + get是request的简化版本 + 一个只能get + 一个通用 + +``` +## 6.3 操作题 + +1. **静态服务器**:创建一个Web服务器,返回HTML页面,包含CSS样式。 +```js +import http from 'http' + +import fs from 'fs' +//1. **静态服务器**:创建一个Web服务器,返回HTML页面,包含CSS样式。 + let server =http.createServer((req,res)=>{ + //读 + + function read(){ + let filePath='./index.html'; + if(fs.existsSync(filePath)){ + let data=fs.readFileSync(filePath,'utf-8'); + return data + } + return '

读取错误:文件不存在

'; + } + res.writeHead(200,{'Content-Type':'text/html; charset=utf-8'}); + res.end(read()) + }) + server.listen(3000, () => { + console.log('服务器运行在 http://localhost:3000'); + }); +``` +- ![](https://gitee.com/fish-ovo_1_0/pig-go/raw/master/image/20260322162749273.png) + +2. **计算器API**:创建RESTful API,支持加、减、乘、除运算。 +```js +import http from 'http'; + +// 创建 web 服务器 +let app = http.createServer((req, res) => { + // 解析 URL + let url = new URL(req.url, `http://${req.headers.host}`); + let method = req.method; + let pathname = url.pathname; + + // 获取查询参数 + let aParam = url.searchParams.get('a'); + let bParam = url.searchParams.get('b'); + + // 转换参数为数字并验证 + let a = parseFloat(aParam); + let b = parseFloat(bParam); + let result; + let response; + + // 根据路径执行运算 + if (pathname === '/add') { + result = a + b; + response = JSON.stringify({ operation: 'add', a, b, result }); + } else if (pathname === '/sub') { + result = a - b; + response = JSON.stringify({ operation: 'sub', a, b, result }); + } else if (pathname === '/mul') { + result = a * b; + response = JSON.stringify({ operation: 'mul', a, b, result }); + } else if (pathname === '/div') { + if (b === 0) { + res.writeHead(400, { 'Content-Type': 'application/json; charset=utf8' }); + res.end(JSON.stringify({ error: '除数不能为零' })); + return; + } + result = a / b; + response = JSON.stringify({ operation: 'div', a, b, result }); + } else { + res.writeHead(404, { 'Content-Type': 'application/json; charset=utf8' }); + res.end(JSON.stringify({ error: '无效的操作路径,请使用 /add, /sub, /mul, /div' })); + return; + } + + // 返回成功响应 + res.writeHead(200, { 'Content-Type': 'application/json; charset=utf8' }); + res.end(response); +}); + +// 监听端口 +let port = 3000; +app.listen(port, () => { + console.log(`服务器运行在 http://localhost:${port}`); +}); +``` +- ![](https://gitee.com/fish-ovo_1_0/pig-go/raw/master/image/20260322181835285.png) + +3. **图片服务器**:创建一个简单的图片服务器,根据URL返回指定目录下的图片。 +```js +import http from 'http'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const IMAGE_DIR = path.join(__dirname, 'images'); +const PORT = 3000; + +if (!fs.existsSync(IMAGE_DIR)) { + fs.mkdirSync(IMAGE_DIR); + console.log(`创建图片目录: ${IMAGE_DIR}`); +} + +const server = http.createServer((req, res) => { + // 安全处理请求路径 + let filePath = req.url === '/' ? '' : req.url; + // 防止目录遍历攻击 + filePath = path.normalize(filePath).replace(/^(\.\.(\/|\\|$))+/, ''); + const fullPath = path.join(IMAGE_DIR, filePath); + + fs.stat(fullPath, (err, stats) => { + if (err || !stats.isFile()) { + res.writeHead(404); + res.end('图片不存在'); + return; + } + + const ext = path.extname(fullPath).toLowerCase(); + const mimeTypes = { + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.png': 'image/png', + '.gif': 'image/gif', + '.webp': 'image/webp', + '.svg': 'image/svg+xml', + '.bmp': 'image/bmp', + '.ico': 'image/x-icon' + }; + const contentType = mimeTypes[ext] || 'application/octet-stream'; + + res.writeHead(200, { 'Content-Type': contentType }); + const stream = fs.createReadStream(fullPath); + stream.pipe(res); + stream.on('error', (streamErr) => { + console.error('流错误:', streamErr); + if (!res.headersSent) { + res.writeHead(500); + res.end('服务器内部错误'); + } + }); + }); +}); + +server.listen(PORT, () => { + console.log(`图片服务器运行在 http://localhost:${PORT}`); + console.log(`将图片放入 ${IMAGE_DIR} 目录,然后访问 http://localhost:${PORT}/图片名.jpg`); +}); +``` +- ![](https://gitee.com/fish-ovo_1_0/pig-go/raw/master/image/20260322182538010.png) +4. **天气查询**:创建一个天气查询工具,调用第三方API获取天气信息。 +```js +import readline from 'readline'; + +const API_KEY = '1fa7ea5066e7f0036e9b0a50d9a25bc9'; // 你的有效密钥 +const BASE_URL = 'https://api.openweathermap.org/data/2.5/weather'; + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); + +async function getWeather(city) { + const url = `${BASE_URL}?q=${encodeURIComponent(city)}&appid=${API_KEY}&units=metric&lang=zh_cn`; + + try { + const response = await fetch(url, { + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' + } + }); + const weather = await response.json(); + + if (weather.cod !== 200) { + console.error(`错误:${weather.message}`); + } else { + console.log(`\n城市:${weather.name}`); + console.log(`天气:${weather.weather[0].description}`); + console.log(`温度:${weather.main.temp}°C`); + console.log(`湿度:${weather.main.humidity}%`); + console.log(`风速:${weather.wind.speed} m/s`); + } + } catch (err) { + console.error('请求失败:', err.message); + } +} + +rl.question('请输入城市名(英文或中文):', (city) => { + if (city) getWeather(city); + rl.close(); +}); +``` +- ![](https://gitee.com/fish-ovo_1_0/pig-go/raw/master/image/20260322190250916.png) + +5. **代理服务器**:创建一个简单的代理服务器,将请求转发到另一个服务器。 + 看不懂 \ No newline at end of file