GUI

下载electron

图形用户界面(Graphical User Interface,简称 GUI,又称图形用户接口)是指采用图形方式显示的计算机操作用户界面
与 CLI 相比,图形界面对于普通用户在视觉和操作上更加容易接受
基于Node.js的GUI框架
NW.JS(Node-Webkit)
Electron
npm init
设置项目入口文件:
“main”: “index.js”
项目入口文件是 Electron 第一个加载的文件,是整个项目的入口

使用HTML、CSS、JavaScript来构建 UI、处理与用户的交互,同时不约而同的使用了开源浏览器 Chromium
使用 Node.js 来访问 浏览器 之外的内容,比如系统、文件、网络等等……

  主进程与渲染进程
  在 Electron 中,被 Electron 直接运行的脚本(package.json 中指定的 main 脚本)被称为主进程
  在 Electron 中用来展示界面的 web 页面都运行在一个独立的,属于它自己的渲染进程中
  我们可以通过主进程来创建 web 页面,但一个 web 页面被销毁的时候,对应的渲染进程也会被终止
  主进程管理所有的 web 页面和它们对应的渲染进程
  一个应用程序有且仅有一个主进程
   在 Electron 中,Electron 同时为 主进程 与 渲染进程暴露了 Node.js 的所有接口,也就是说,
   我们可以在 Electron 的主进程 与 渲染进程 中使用 Node.js 的 API
    npm i electron
  require('electron')

/*
app 对象
该对象提供了一系列的事件用来控制整个应用程序的生命周期,从打开到关闭,如:
ready、window-all-closed、quit……
同时也提供了一些方法来管理应用程序的状态与行为,如:
quit()、relaunch()、hide()、show()……
BrowserWindow 类
创建和控制浏览器窗口
new BrowserWindow( [options] )
options:窗口选项 - https://electronjs.org/docs/api/browser-window
*/

console.log('hi');
/**
 * 事件 属性
 */
const {app,BrowserWindow}=require('electron')
/*
app 对象
  该对象提供了一系列的事件用来控制整个应用程序的生命周期,从打开到关闭,如:
  ready、window-all-closed、quit……
  同时也提供了一些方法来管理应用程序的状态与行为,如:
  quit()、relaunch()、hide()、show()……
  BrowserWindow 类
  创建和控制浏览器窗口
  new BrowserWindow( [options] )
  options:窗口选项 - https://electronjs.org/docs/api/browser-window
  BrowserWindow 对象
  每一个 BrowserWindow 对象的实例都是一个独立的渲染进程,
  同时该对象也提供了各种用于操控的 API,包括:事件、属性、方法
  实例事件
  close、closed、focus、blur、show、hide……
  实例属性
  webContents:窗口包含的内容对象
  id:窗口的唯一 ID
  实例方法
  close()、show()、hide()、maximize()、unmaximize()、setSize()、getSize()、
  setPosition()、getPosition()、setTitle()、getTitle()
  loadFile() : 加载页面(这就是我们要显示的内容了),页面地址使用相对路径,
  相对路径相对于应用程序根目录
  loadURL() : 使用 URL 协议加载文件,可以是 http 协议,也可以是 file 协议
  无边框窗口
  BrowserWindow 的 options
  frame : false
  透明窗口
  BrowserWindow 的 options
  transparent : true
  父子窗口
  BrowserWindow 的 options
  parent: 父窗口对象
  子窗口永远显示在父窗口的前面
  模态窗口
  BrowserWindow 的 options
  parent: 父窗口对象
  modal : true
  子窗口会禁用父窗口
*/
app.on('ready',()=>{
  const bw=new BrowserWindow({
    title:'丸子'
  })
  // setTimeout(()=>{
  //   app.quit()
  // },2000)
  bw.loadFile('./layout/index.html')
    //开发者工具
  bw.webContents.openDevTools()
  //支持加载远程文件,http协议
  // bw.loadURL('http://www.baidu.com')
})
console.log('hi');
/**
 * 事件 属性
 */
const {app,BrowserWindow,Menu,MenuItem}=require('electron')

//主线程

let username='丸子丸子丸子'
app.on('ready',()=>{
  const bw=new BrowserWindow({
    title:'丸子'
  })
  // setTimeout(()=>{
  //   app.quit()
  // },2000)
  // bw.loadFile('./layout/index.html')
  let m1=new Menu()
   /*
 // new MenuItem( [options] )
  //options : https://electronjs.org/docs/api/menu-item
 // 当菜单有子菜单的时候,父菜单的 type 应设置为 submenu
 添加菜单项目到指定菜单
  菜单实例.append(菜单项)
  菜单实例.insert(位置, 菜单项)
  把菜单添加到应用程序顶层
  Menu.setApplicationMenu(menu对象)
  菜单实例.popup(options)
  options:
  window:指定窗口
  x/y : 位置
  callback : 关闭后的回调
  菜单实例.closePopup()
  关闭上下文菜单
  快速构建菜单项
  Menu.buildFromTemplate(template)
  template:
  template是一个数组,用于快速构建 MenuItem
  */
  let mi1=new MenuItem({
    type:'normal',
    label:'文件',

  })
  m1.append(mi1)
//开发者工具
  bw.webContents.openDevTools()
  bw.loadFile('./layout/index.html')

  //指定该菜单显示的主体
  //把菜单添加到应用程序窗口最顶层
  Menu.setApplicationMenu(m1)

  //支持加载远程文件,http协议
  // bw.loadURL('http://www.baidu.com')
})

layout/index.html

<!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>胖</title>
</head>
<body>
  我是小胖
  
</body>
<script>
  //渲染线程中不能直接获取主进程的数据
console.log(username);

</script>
</html>

这样是报错的。渲染线程中不能直接获取主进程的数据

把index里面的username设置成全局的

electron 下的remote 通过getGlobal获取主进程的数据

  //渲染线程中不能直接获取主进程的数据
// console.log(username);

//渲染进程也可以使用electron对象 ,electron对象下面有的属性只能在主进程使用,有的是在渲染进程中用的
const {remote}=require('electron')
// console.log(ele);
//electron 下的remote 通过getGlobal获取主进程的数据
console.log(remote.getGlobal('username'));

index.html
在这里插入图片描述
通过监听事件方法获取数据
index.js

console.log('hi');
/**
 * 事件 属性
 */
const {app,BrowserWindow,Menu,MenuItem,ipcMain}=require('electron')

//主线程

// global.username='丸子丸子丸子'
let datas={
  username:'丸子丸子丸子'
}
app.on('ready',()=>{
  const bw=new BrowserWindow({
    title:'丸子'
  })
  // setTimeout(()=>{
  //   app.quit()
  // },2000)
  // bw.loadFile('./layout/index.html')
  let m1=new Menu()
  let mi1=new MenuItem({
    type:'normal',
    label:'文件',

  })
  m1.append(mi1)
//开发者工具
  bw.webContents.openDevTools()
  bw.loadFile('./layout/index.html')

  //指定该菜单显示的主体
  //把菜单添加到应用程序窗口最顶层
  Menu.setApplicationMenu(m1)

  //支持加载远程文件,http协议
  // bw.loadURL('http://www.baidu.com')

  //监听渲染进程 
  ipcMain.on('getData',function(e,key){
    // console.log(data);
    // console.log('8888');
    e.sender.send('sendData',datas[key])

    
  })



  setTimeout(()=>{
    bw.webContents.send('hi','hi....',444,55,666)
  },2000)
})

layout/index.html

<!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>胖</title>
</head>
<body>
  我是小胖
  
  <button>小胖2</button>
</body>
<script>
  //渲染线程中不能直接获取主进程的数据
// console.log(username);

//渲染进程也可以使用electron对象 ,electron对象下面有的属性只能在主进程使用,有的是在渲染进程中用的
const {remote,ipcRenderer}=require('electron')
// console.log(ele);
//electron 下的remote 通过getGlobal获取主进程的数据
// console.log(remote.getGlobal('username'));
//ipc

var btn=document.querySelectorAll('button')
btn[0].onclick=function(){
  // alert('4')
  ipcRenderer.send('getData','username')
}
ipcRenderer.on('sendData',function(e,data){
console.log(data);

})
ipcRenderer.on('hi',(e,...data)=>{
  console.log(data);
  
})
</script>
</html>

页面与页面之间的通讯亦可用localStorage

一个小的todolist

electron.build用来打包
https://www.electron.build/configuration/configuration#configuration 相关配置 
    "win": {
      "icon": "./source/logo.ico",
        "target": [
          "nsis",
          "zip"
        ]
      },
      "nsis": {
        "oneClick": false,
        
        "allowToChangeInstallationDirectory": true, // 是否允许用户默认安装 目录
        // 是否允许用户默认安装 图标 
        "installerIcon": "./source/logo.ico",
        // 是否允许用户默认安装 目录
        "installerHeader": "./source/header.bmp",
        // 授权信息
        "license": "./source/license.txt"
      }

自己写的todolistdemo可以来看一下
链接:https://pan.baidu.com/s/1Ym2bR1y2au9OHdsXTobQfQ
提取码:iy9j
复制这段内容后打开百度网盘手机App,操作更方便哦
命令npm run dev

node网络

为了能够让不同的计算机之间进行数据共享(分享),就需要在不同的计算机之间建立一种连接,通过某种方式连接在一起的计算机之间就组成了一个网络,在同一个网络中的计算机就可以通过一些制定好的规则进行通信与数据传输了
两个重点

  • 连接(定位)
  • 传输

如何连接?
无论哪种方式,都需要在电脑中有对应的硬件设备来处理这些要共享(传输)的数据

  • 网线/无线 => 网卡(无线网卡)
  • 蓝牙 => 蓝牙模块(硬件)
    这些硬件的一个重要作用就是用来连接和转发数据的
    要传输数据,首先最重要的是能够让不同的计算机之间能定位找到对方,所以这些用来联网的设备都会有一个用来标识自己在某个网络中的位置的方式
    网卡
  • 每一块网卡都有一个自己的唯一编号(地址):MAC
  • 每一个块网卡在连接到某个网络的时候会拥有一个固定(或动态分配)的编号(地址):IP 地址

如何定位?
IP
Internet Protocol,也称为网络协议
IP协议
所谓协议就是一套规则,而IP协议就是为了能够让计算互连的一种规则,为了标识每台连入网络中的计算机(网卡)的唯一性,每个连入网络的网卡都会绑定一个IP地址(固定-买断、临时分配)
IP地址格式
x.x.x.x,x 表示一个字节的值,一个字节是 8 位,所以一个 x 能表示的值为:0-255 之间,一个 IP地址 由4个字节组成,所以 IP地址的表示范围为:0.0.0.0 - 255.255.255.255,这套规则目前是 IP 协议的第四个版本中的定义,所以也称为:IPV4,不过就现在计算机发展来说,这套规则能够表示的地址已经很明显不够使用了,所以最新的一套IP协议规则是IPV6

如何连接?
IP分配使用
IP 地址的使用是有一定规则和规划的 - 127.0.0.1 : 本地回环网络地址(其实就是自己CALL自己的一个快捷方式)

  • (192.168.0.1 - 192.168.255.254 / 172.16.0.1 - 172.31.255.254 / 10.0.0.1 - 10.255.255.254)局部网络(局域网)使用

监听
一台计算机的数据都是通过网卡等设备进行传输的(发送和接受),那么也就是意味着我们电脑中运行的各种不同的软件使用可能都是同一块网卡
为了解决程序与程序之间数据传输,所以一个应用程序在使用网卡发送和接收数据的时候,需要指定你要监听的网卡(一台机器可能有多块网卡),同时还要指定一个端口号

端口
类似我们去银行办理业务的窗口,不同的端口为不同的应用程序进行服务,当一个程序需要接收来自某块网卡设备的数据的时候,需要指定监听的端口,同时发送数据到某个地址(网卡)的数据也要指定接收方的端口号
发送数据的端口由系统分配
一个应用程序可以同时监听多个网卡的多个端口
一个端口只能同时被一个程序监听
如果一个程序尝试监听一个已经被其他程序监听的端口,就会报端口占用的错误

数据传输协议
有了网络定位协议,然后再需要做的是定义和选择一个数据的传输协议了

  • TCP
  • 可靠的、面向连接的协议、传输效率低
  • 效率要求相对低,但对准确性要求相对高的场景
  • 文件传输、接受邮件、远程登录
  • UDP
  • 不可靠的、无连接的服务、传输效率高
    -效率要求相对高,对准确性要求相对低的场景
  • 在线视频、网络语音电话

dgram (数据报)
dgram模块提供了 UDP 数据包 socket 的实现
socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,其本质上就是一套用于实现网络数据交换的接口(API)

使用
const dgram = require(‘dgram’)
client.js

const dgram=require('dgram');
/* 创建一个scoket类    
通过scoket我们就可以对网络数据进行读取和输出 
udp:无连接协议,不需要连接到服务器,然后再发数据 
*/
// const socket=new dgram.Socket
const clientSocket=dgram.createSocket('udp4');//udp4 =>ipv4
clientSocket.send('hi',12345,'127.0.0.1')
//clientSocket传送数据

server.js

const dgram=require('dgram');
/* 创建一个scoket类    
通过scoket我们就可以对网络数据进行读取和输出 
*/
// const socket=new dgram.Socket

const serverSocket=dgram.createSocket('udp4');//udp4 =>ipv4
serverSocket.on('listening',()=>{
  console.log('服务器开启成功,等数据:');

})
serverSocket.on('message',data=>{
console.log('我有了',data.toString());

})

serverSocket.bind(12345,'127.0.0.1')
//serverSocket 端口号 地址

Net 模块(TCP)
net 模块提供了创建基于流的 TCP 或 IPC 服务器(net.createServer())和客户端(net.createConnection()) 的异步网络 API
使用
require(‘net’)

  • 服务端:提供服务,被连接,被请求的一方
  • 客户端:获取服务,发起连接,请求的一方
    Net 模块(TCP)
    net.Server 类
    创建服务端对象
    const server = new net.Server()
    const server = net.createServer([port[, host]])
    监听端口,处理请求
    server.listen(端口, [ip])
    端口:
    ip:默认为0.0.0.0,表示所有
    创建客户端端对象
    const socket = new net.Socket()
    const socket = net.createConnection(port[, host][, connectListener])
    net.Socket 类
    write(data[, encoding][, callback])
    在 socket 上发送数据。第二个参数制定了字符串的编码 - 默认是 UTF8 编码
    end([data][, encoding])
    半关闭 socket。例如发送一个 FIN 包。服务端仍可以发送数据
    如果指定了 data,则相当于调用 socket.write(data, encoding) 之后再调用 socket.end()
    Net 模块(TCP)
    数据包
    在数据传输过程中不仅仅只有主体数据(你要发送的主要内容),还包括了一些其他的数据信息,比如发送端的IP、端口等,以方便接受者对数据进行处理与回复
    如果发送的数据比较大的话,还会对发送的数据进行分包,每一个包中包含有一部分主体数据以及上面提到的额外信息,接收方在接收到数据以后会数据包进行整合等一系列操作
    这种传输规则就是数据传输协议中的规定,不同的协议对传输规则有不同的规定
    socket.remoteAddress
    socket.remotePort
    server.js
const net=require('net');
const fs=require('fs')
/* 创建服务器端
tcp要通过net
1监听地址以及端口
2 处理发送到当前监听地址以及端口的数据
3返回(发送数据)到连接的客户端

net.server的类可以
new net .Server()=>return net.Server
*/

const server=net.createServer(()=>{
  //这个函数是connection绑定的函数aaaaaa
});

//当有客户端连接的时候触发
server.on('connection',socket=>{
  console.log('有人连接了我');
  let data2=fs.readFileSync('./demo.jpg')
  // console.log(data2);
  // socket.write(data2)
  socket.end()
  socket.on('data',data=>{
   
    console.log('hi');
    // socket.write()
  // socket.write('show me money');
  // if(data.toString()=='getPic'){
    // console.log('发送图片给客户端');
    //发送一个图片给客户端


    
  // }

  })
})
/* 监听地址及端口 */
server.listen(789,'127.0.0.1')

client.js

const net=require('net');
/**
 * 创建客户端与udp不同
 * 1,new net.Socket();
 * new.creatConnection()
 */

 const clientsocket=net.createConnection(789,'127.0.0.1')


 //监听数据传输
 clientsocket.on('data',data=>{
  //  console.log('服务器返回',data.toString());
  //  clientsocket.write('get money')
  // clientsocket.write('getPic')
  // clientsocket.write('getPic')
console.log('shuju',data);

  //拼装buffer数据
 })

 clientsocket.on('end',data=>{
   console.log('数据传输完成');
   //把接收到的数据组合起来,通过fs写入到client文件夹中
   //我们接收到的数据是buffer

 })

 //当数据包接收完成的时候触发
//  clientsocket.on('end',()=>{
//    console.log('数据传输完成');
   
//  })

HTTP
HTTP的结构
HTTP是一个基于TCP/IP通信协议来传输(超文本)数据

  • HTTP是基于TCP/IP协议来定位传输数据
  • 同时在TCP/IP包基础上对要传输的数据进行再次包装
  • HTTP是单向单链接、无状态协议
    http 模块
    require(‘http’)
    http.Server 类
    const server = new http.server()
    const server = http.createServer([options][, requestListener])
    该类继承自 net.server,http基于tcp
    监听
    server.listen([port][, host])
    server.js
const http=require('http');

const server=http.createServer();

//request 监听的对象
server.on('request',()=>{
  console.log('接收到了请求');
  
})

//80默认约定给http使用的
server.listen(80,'0.0.0.0')

client.js

const http=require('http');
const fs=require('fs')
/**
 * 发起请求
 *  
 */

 //创建请求,发http请求的对象
const client=http.request({
  //tcp中的设置
  //host:'127.0.0.1',
  host:'www.baidu.com',
  
  port:80,
  protocol:'http:',
  method:'get',
  // path:'/'
  path:'/img/bd_logo1.png'

},res=>{
  //服务器响应的时候触发
  let consten=Buffer.alloc(0)

  res.on('data',data=>{
    // consten+=data.toString()
     consten=Buffer.concat([consten,data],consten.length+data.length)
  //  console.log(data.toString());
    
  })
  res.on('end',data=>{
    fs.writeFileSync('./baidu.png',consten)
  //  console.log(data.toString());
    
  })

})

client.write('abc')
client.end()

request.socket.remoteAddress

http路由

app.js

/**
 * web server 提供web浏览器服务的工具
 */

 const http=require('http');
 //fs是文件系统
 const fs=require('fs');
 const Mime=require('./libs/Mime.js');
 //这个就是比如后端从数据库拿来出的数据
let users=[
  {
    name:'xiaopang',
    gender:'女',
    skills:['23','设计','你好']
  },
  {
    name:'xiaopang2',
    gender:'女',
    skills:['23','设计2','你好2']
  }
]
  // let mime =new Mime();
  //不同类别后面的东西不一样
console.log(Mime.getType('txt'));
console.log(Mime.getType('html'));



 /**
  * 创建http服务器
  */
 const app=http.createServer((req,res)=>{
   /**
    * 有用户请求触发的函数
    * req 保存和提供了当前请求的客户端信息
    * res 保存和提供了响应的相关方法
    */
   //console.log('有人触发了');

  //  console.log(req.httpVersion);
  //  console.log(req.method);
  //  console.log(req.url);
  //  console.log(req.rawHeaders);
   /**
    * 向客户端发送数据则需要使用res
    */
   console.log(req.url);

   /**
    * 
    * 设置头信息,不然乱码
   res.writeHead(状态码,状态码描述,头信息)
    * 
    */
   res.writeHead(200,http.STATUS_CODES[200],{
     'Content-Type':'text/html;charset=utf-8'
   })

   //通过fs读取对应的文件,返回客户端
   //我们约定static文件夹里面是静态文件,通过读取本地文件即可
   let content=''
   if(req.url.startsWith('/static')){
    staticsend(__dirname+req.url)

   }else{
     //动态
     switch(req.url){
       case '/user':
       res.writeHead(200,http.STATUS_CODES[200],{
        'Content-Type':'application/json'
      })
      let data=users.map(user=>user.name)
       res.end(JSON.stringify(data))
       
       break
       case '/getbaidu':
       const r=http.request({
         host:'www.baidu.com'
       },function(baidures){
         let data=''
        baidures.on('data',(chunk)=>{
          data+=chunk.toString()
        })
        baidures.on('end',(chunk)=>{
          res.end(data)
        })
       })
       r.end()
       break
     }
   }
  //  switch (req.url){
  //    case'/':
  //   //  content=fs.readFileSync(__dirname+'/static/index.html')
  //    staticsend(__dirname+'/static/index.html')
  //    res.end(content);
  //    break;
  //    case'/list':
  //   //  content=fs.readFileSync(__dirname+'/static/list.html')
  //   staticsend(__dirname+'/static/list.html')
  //    res.end(content);
  //    break;
  //    case'/view':
  //   //  content=fs.readFileSync(__dirname+'/static/view.html')
  //    staticsend(__dirname+'/static/view.html')
  //    res.end(content);
  //    break;
  //    case'/index.css':
  //   //  content=fs.readFileSync(__dirname+'/static/index.css')
  //    staticsend(__dirname+'/static/index.css',{
  //     'Content-Type':'text/css;charset=utf-8'
  //    })
  //    res.end(content);
  //    break;
  //    default:
  //   //  content=fs.readFileSync(__dirname+'/static/404.html')
  //    staticsend(__dirname+'/static/404.html',{},404)
  //    res.end(content);
  //    break;
  //  }

   function staticsend(filename,headers={'Content-Type':'text/html;charset=utf-8'},statusCode=200){
     if(fs.existsSync(filename)){
       let ext=filename.substring(filename.lastIndexOf('.')+1)
       console.log(ext);
       if(!ext){
         ext='txt'
       }
       headers['Content-Type']=Mime.getType(ext)
      res.writeHead(statusCode,http.STATUS_CODES[statusCode],headers)
      content=fs.readFileSync(filename)
      res.end(content);
     }else{
      staticsend(__dirname+'/static/404.html',{},404)
     }
 
   }
  //  res.end('hi');
   

   /**
    * 我们需要根据不同的url返回客户端不同的数据
    * 
    */
   
   
   
 });

 /**
  * 指定app监听的端口以及网络
  */
 app.listen(80,()=>{
   console.log('服务器启动成功了');
 })

static/list.html

<!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>
  <link rel="stylesheet" href="index.css">
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

</head>
<body>
  我是列表页哦
  <div id="app">
    <h1></h1>
    <ul>
      <li v-for="user of users">
        {{user}}
      </li>
    </ul>
  </div>
  <script>
   new Vue({
     el:'#app',
     data:{
       users:[

       ]
     },
     created(){
       //fetch请求
       fetch('/user').then(res=>{
        console.log(res);
        //返回promise对象
         return res.json()
       }).then(data=>{
        this.users=data

       })
     }
   })
  </script>
</body>
</html>
//热重载   supervisor npm i -g supervisor  supervisor  app就可以使用了

热重载 supervisor npm i -g supervisor supervisor app就可以使用了
static/1.html

<!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>
  <button>按钮</button>

  <script>
    const button=document.querySelector('button')
    button.onclick=()=>{
        fetch('/getbaidu').then(res=>{
          console.log(res);
          return res.text()
        }).then(data=>{
          console.log(data);
          
        })
    }
  </script>
</body>
</html>

还有404,index.view.html不做详写,只是为了加载不同的
libs/Mime.js

const _mime={

  " text/plain":['txt'],
  " text/html":['htm','html'],
    "text/css":['css'],
    " text/javascript":['js','jsx'],
    "image/png":['png'],
    "image/jpg":['jpg'],

}
// class Mime{

// }
module.exports= {
getType(ext){
  for(let property in _mime){
    let arr=_mime[property]
    if(arr.includes(ext)){
      return property
    }
  }
},
getExtension(type){
  return _mime[type]
}
}

其实Mime可以去github来,但写出来是为了观赏
运行命令node app
链接:https://pan.baidu.com/s/1SCxYMvQwrZtTsziJ4kEUSg
提取码:sgx3
复制这段内容后打开百度网盘手机App,操作更方便哦

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐