0-0.文件上传

0-1.了解文件上传
在前端把本地的  img 视频 音频 之类的文件,发送给服务端, 
	服务端存储在服务器里面,把文件地址存储在数据库里面

1-0.前端文件上传

1-1. 直接使用 form 表单上传

表单直接上传  
      	form 标签的设置
    	action   设置服务器地址
    	method   POST 方式
    	enctype  mutilpart/form-data	属性规定在发送到服务器之前应该如何对表单数据进行编码。
	          => enctype 设置请求头中的 content-type
	          => mutilpart 设置以二进制流的形式上传
  		input
		      => type 选择成 file
		      => name 设置一个名字
<form action="http://localhost:8080/upload" method="POST" enctype="multipart/form-data">
	    <input type="file" name="avatar">
	    <input type="text" name="username">
	    <button>上传</button>
  </form>

1-2. 原生 JS 上传文件

原生 js 上传
      需要用 HTTP/2.0 的技术
      一个内置构造函数叫做 FormData()
	          => 语法: new FormData(form元素)
	          => 返回一个包含 form 元素里面所有带有 name 属性元素的值
	          => 直接打印看不到
<form>
    <input type="file" name="avatar">
    <input type="text" name="username">
    <button>上传</button>
  </form>
<script>
    // 1. 获取元素绑定事件
    const form = document.querySelector('form')
    form.addEventListener('submit', function (e) {
      e.preventDefault()
      // 2. 拿到文件
      const formData = new FormData(this)
      // 3. 正常 ajax 发送
      const xhr = new XMLHttpRequest()
      xhr.open('POST', '地址')
      // 不需要单独设置请求头
      // 因为 formData 会自动设置请求头
      xhr.send(formData)
      xhr.onload = function () {

      }
    })
</script>

1-3. jQuery 上传文件

jquery 上传和原生 js 基本一致
      绑定事件
      使用 FormData() 拿到数据
      使用 $.ajax()

  问题
    + formData 会自动设置请求头, 但是 jquery 也会
      => jquery 会默认设置程 application/x-www-form-urlencoded
    + formData 会发送 二进制流 的形式
      => jquery 会把所有的数据给你直接格式化成 key=value&key=value
<form>
    <input type="file" name="avatar">
    <input type="text" name="username">
    <button>上传</button>
  </form>

  <script src="../../node_modules/jquery/dist/jquery.min.js"></script>
  <script>
    $('form').submit(function (e) {
      e.preventDefault()

      const formData = new FormData($('form')[0])

      $.ajax({
        method: 'POST',
        url: 'http://localhst:8080/upload',
        data: formData,
        contentType: false, // 不要动请求头, 直接上传
        processData: false, // 不要管我的数据, 直接上传
        success (res) {
          console.log(res)
        }
      })
    })
  </script>

2-0.后端文件上传

2-1.单文件上传

单文件上传的简单版本

1. 解决跨域问题
  	+ 下载一个包叫做 cors
2. 接收文件
      2-1. 我要准备一个文件夹, 在服务器上
        	=> 存储上传的文件
      2-2. 需要一个插件帮助
        	=> multer
        	=> 下载
        	=> 导入
      2-3. 需要使用 multer 配置一个接收器
        	=> multer({ dest: '你存放文件的路径' })
      2-4. 使用接收器去接收文件
        	=> 哪一个路由需要接收文件, 配置在哪一个路由上
        	=> 写在路由标识符的后面, 路由处理函数的前面
        	=> 接收器.single('前端上传的文件的 key')
      2-5. 在路由处理函数里面
        	=> 会在 req 上面多家一个信息叫做 file
        	=> 就是你上传的文件的信息
  	注意: 会把你的文件存储起来, 但是没有后缀, 随机命名
// 导入框架
	const express = require('express')
	// 创建路由表
	const router = express.Router()
	// 1. 导入 cors 插件
	const cors = require('cors')
	// 2-2. 导入 multer 插件
	const multer = require('multer')
	// 2-3. 使用 multer 去生成一个接收其
	//      我配置的这个接收器, 将来接收到的文件就直接存储在 指定目录
	const fileUpload = multer({ dest: '../uploads/' })
	// 创建服务
	const app = express()
	
	// 1. 挂载上 cors 就跨域了
	app.use(cors())
	
	// 2-4. 在需要的路由上进行配置
	router.post('/upload', fileUpload.single('avatar'), (req, res) => {
	  console.log('接收请求')
	  console.log(req.file)
	})
	// 使用路由表
	app.use(router)
	// 监听端口号
	app.listen(8080, () => console.log(8080))

2-2.单文件上传

single 方法是专门接收单文件-------> 一个名称配一个文件
单文件上传的复杂版本
    1. 解决跨域问题
        下载一个包叫做 cors
    2. 接收文件
	      2-1. 我要准备一个文件夹, 在服务器上
	        	=> 存储上传的文件
	      2-2. 需要一个插件帮助
	        	=> multer
	        	=> 下载
	        	=> 导入
	      2-3. 生成一个仓库信息
	        	=> multer.diskDtorage({ 配置 })
	        		=> destitnation: function () {}  设定存储路径
	        		=> filename: function () {}  设定文件名称
	        	=> 返回值: 是一个仓库信息
	      2-4. 使用 multer 生成一个接收器
	        	=> 接收器里面配置一个仓库信息
	        	=> 语法: multer({ storage: 仓库信息 })
const express = require('express')
	const path = require('path')
	const router = express.Router()
	// 1. 导入 cors 插件
	const cors = require('cors')
	
	// 2-2. 导入 multer 插件
	const multer = require('multer')
	
	// 2-3. 使用 multer 生成一个仓库信息
	const storage = multer.diskStorage({
	  destination: function (req, file, cb) {
	    // req, 本次请求信息
	    // file, 本次请求的文件
	    // cb, 回调函数, 利用回调函数来设定存储路径
	    // 第一个参数 null, 表示不要修改我的 二进制流 文件
	    cb(null, '../uploads/')
	  },
	  filename: function (req, file, cb) {
	    // req, 本次请求信息
	    // file, 本次上传的文件信息
	    // cb, 回调函数, 通过回调函数来设定文件名称
	    // 从 file 信息里面把后缀名拿出来, 前面我们自己拼接随机数
	    const tmp = path.extname(file.originalname)
	    cb(null, `avatar_${ new Date().getTime() }-${ Math.random().toString().slice(2) }${ tmp }`)
	  }
	})
	
	// 2-4. 配置接收器, 带有仓库信息
	const fileUpload = multer({ storage :storage})
	
	const app = express()
	
	// 1. 挂载上 cors 就跨域了
	app.use(cors())
	
	// 2-5. 使用我们配置好的接收其去接收文件
	router.post('/upload', fileUpload.single('avatar'), (req, res) => {
	  console.log('接收请求')
	  console.log(req.file)
	})
	
	app.use(router)
	
	app.listen(8080, () => console.log(8080))

2-3.单名称多文件上传

array 方法是专门接收多文件----> 一个名称配多个文件
单名称多文件上传 
    1. 解决跨域问题
      + 下载一个包叫做 cors
    2. 接收文件
	      2-1. 我要准备一个文件夹, 在服务器上
	        	=> 存储上传的文件
	      2-2. 需要一个插件帮助
	        	=> multer
	        	=> 下载
	       		=> 导入
	      2-3. 生成一个仓库信息
	        	=> multer.diskDtorage({ 配置 })
	          		-> destitnation: function () {}  设定存储路径
	          		-> filename: function () {}  设定文件名称
	        	=> 返回值: 是一个仓库信息
	      2-4. 使用 multer 生成一个接收器
	        	=> 接收器里面配置一个仓库信息
	        	=> 语法: multer({ storage: 仓库信息 })
	      2-5. 使用方法发生一些变换
	        	=> single 方法是专门接收单文件
	          		-> 一个名称配一个文件
	        	=> array 方法是专门接收多文件
	          		-> 一个名称配多个文件
	        	=> 在后面的路由处理函数里面就不能接收 req.file
	          		-> file 只是接收单文件
	          		-> files 接收多文件(以一个数组的形式, 里面存储着每一个文件信息)
const express = require('express')
	const path = require('path')
	const router = express.Router()
	// 1. 导入 cors 插件
	const cors = require('cors')
	
	// 2-2. 导入 multer 插件
	const multer = require('multer')
	
	// 2-3. 使用 multer 生成一个仓库信息
	const storage = multer.diskStorage({
	  destination: function (req, file, cb) {
	    // req, 本次请求信息
	    // file, 本次请求的文件
	    // cb, 回调函数, 利用回调函数来设定存储路径
	    // 第一个参数 null, 表示不要修改我的 二进制流 文件
	    cb(null, '../uploads/')
	  },
	  filename: function (req, file, cb) {
	    // req, 本次请求信息
	    // file, 本次上传的文件信息
	    // cb, 回调函数, 通过回调函数来设定文件名称
	    // 从 file 信息里面把后缀名拿出来, 前面我们自己拼接随机数
	    const tmp = path.extname(file.originalname)
	    cb(null, `avatar_${ new Date().getTime() }-${ Math.random().toString().slice(2) }${ tmp }`)
	  }
	})
	// 2-4. 配置接收器, 带有仓库信息
	const fileUpload = multer({ storage })
	const app = express()
	// 1. 挂载上 cors 就跨域了
	app.use(cors())
	// 2-5. 使用我们配置好的接收其去接收文件
	router.post('/upload', fileUpload.array('avatar'), (req, res) => {
	  console.log('接收请求')
	  console.log(req.file)
	  console.log(req.files)
	})
	app.use(router)
	app.listen(8080, () => console.log(8080))

2-4多名称多文件上传

在后面的路由处理函数里面就不能接收 req.file
-> file 只是接收单文件
      -> files 接收多文件(以一个数组的形式, 里面存储着每一个文件信息)

多名称多文件上传
1. 解决跨域问题
  + 下载一个包叫做 cors
2. 接收文件
  2-1. 我要准备一个文件夹, 在服务器上
    => 存储上传的文件
  2-2. 需要一个插件帮助
    => multer
    => 下载
    => 导入
  2-3. 生成一个仓库信息
    => multer.diskDtorage({ 配置 })
      -> destitnation: function () {}  设定存储路径
      -> filename: function () {}  设定文件名称
    => 返回值: 是一个仓库信息
  2-4. 使用 multer 生成一个接收器
    => 接收器里面配置一个仓库信息
    => 语法: multer({ storage: 仓库信息 })
  2-5. 使用方法发生一些变换
    => single 方法是专门接收单文件
      -> 一个名称配一个文件
    => array 方法是专门接收多文件
      -> 一个名称配多个文件
    => fields 方法是专门接收多文件
      -> 多个名称配多个文件
    => 在后面的路由处理函数里面就不能接收 req.file
      -> file 只是接收单文件
      -> files 接收多文件(以一个数组的形式, 里面存储着每一个文件信息)
注意: 涉及到多个名称, 我们要一个一个标志好
const express = require('express')
	const path = require('path')
	const router = express.Router()
	// 1. 导入 cors 插件
	const cors = require('cors')
	// 2-2. 导入 multer 插件
	const multer = require('multer')
	// 2-3. 使用 multer 生成一个仓库信息
	const storage = multer.diskStorage({
	  destination: function (req, file, cb) {
	    // req, 本次请求信息
	    // file, 本次请求的文件
	    // cb, 回调函数, 利用回调函数来设定存储路径
	    // 第一个参数 null, 表示不要修改我的 二进制流 文件
	    cb(null, '../uploads/' + file.fieldname)
	  },
	  filename: function (req, file, cb) {
	    // req, 本次请求信息
	    // file, 本次上传的文件信息
	    // cb, 回调函数, 通过回调函数来设定文件名称
	    // 从 file 信息里面把后缀名拿出来, 前面我们自己拼接随机数
	    const tmp = path.extname(file.originalname)
	    cb(null, `${ file.fieldname }_${ new Date().getTime() }-${ Math.random().toString().slice(2) }${ tmp }`)
	  }
	})
	// 2-4. 配置接收器, 带有仓库信息
	const fileUpload = multer({ storage })
	const app = express()
	// 1. 挂载上 cors 就跨域了
	app.use(cors())
	// 2-5. 使用我们配置好的接收其去接收文件
	router.post('/upload', fileUpload.fields([
	  { name: 'avatar' },
	  { name: 'photo' }
	]), (req, res) => {
	  console.log('接收请求')
	  console.log(req.file)
	  console.log(req.files)
	})
	app.use(router)
	app.listen(8080, () => console.log(8080))