本文编写于 673 天前,最后修改于 669 天前,其中某些信息可能已经过时。

小程序云开发 云数据库获取集合写入excel进行邮件发送

给别人(友好的兄弟部门)写了一个投票小程序,全程使用云开发平台,也就是说不需要自己再挂服务器了,投票数据需要最后进行统计,之前是使用csv进行导出,发送邮件到指定邮箱,由相关人员对csv文件进行处理(倒入excel,另存为)进行数据统计,但是实际操作发现太麻烦了,于是乎想到用云开发的云储存进行excel表的写入,让统计数据的同学下载这个文件就行了,说干就干

图文无关,用做封面

这个坑是我自己一步一个脚印踩出来的,记下来便于以后使用

  1. 云数据库一次服务器端查询最多支持一百条数据,如果数据超过100条(如本项目)得进行多次查询

  2. 云储存的文件名不能有特殊字符,否则无法下载,如时间日期2019-11-12 12:30.xls有短横杠有空格,腾讯会拒绝访问

  3. 云储存得到的链接是cloud://xxx,需要在服务端或小程序端解析成真正地址才能在外部下载。

坑的解析

坑1

超过100条数据的查询,看似很复杂,要先读有多少数据,判断需要读多少次,然后读取这个次数,加入一个数组,最后全部输出。

但其实官方给了个demo,改就行了,反正我不会写hhh,我会用就行。

官方文档

因为有默认 limit 100 条的限制,因此很可能一个请求无法取出所有数据,需要分批次取:

这里我就给自己改的了,如果数据很少,可以用下面注释的方法

  //取集合所有数据,突破一次100条的限制
  const countResult = await db.collection('eventVote').count()
  const total = countResult.total
  // 计算需分几次取
  const batchTimes = Math.ceil(total / 100)
  // 承载所有读操作的 promise 的数组
  const tasks = []
  for (let i = 0; i < batchTimes; i++) {
    const promise = db.collection('eventVote').skip(i * MAX_LIMIT).limit(MAX_LIMIT).get()
    tasks.push(promise)
  }
  // 等待所有
  var myData = (await Promise.all(tasks)).reduce((acc, cur) => {
    return {
      data: acc.data.concat(cur.data),
      errMsg: acc.errMsg,
    }
  })

  // 一次只能取100条数据,对于更多的数据集合需要用上面的方法
  // try {
  //   var myData = await db
  //     .collection('eventVote')
  //     .orderBy('datetime', 'desc')
  //     .orderBy('event_id', 'desc')
  //     .orderBy('school', 'desc')
  //     .orderBy('title', 'desc')
  //     .get()
  //   //console.log(JSON.stringify(myData))
  // } catch (err) {
  //   console.error(err)
  // }

坑2

坑2 比较简单解决,我这里文件命名是按照时间的顺序来命名的

把2019-11-24 10:30:30 这种去掉输出符号和空格就行,最后变成

let d = new Date().Format("yyyyMMddhhmmss")
//1,定义excel表格名
let dataCVS = 'excel/result' + d + '.xlsx'

这样的话输出的文件就是没有空格和特殊符号的了,可以正常下载了

坑3

传入文件的cloud链接即可获得真实下载的地址,贴入发送的邮件即可。

//获取真实的下载地址
  const fileUrl = [fileres.fileID]
  const result = await cloud.getTempFileURL({
    fileList: fileUrl,
  })
  var downloadUrl = result.fileList[0].tempFileURL
  console.log(result.fileList)

完整代码

// 云函数入口文件
const cloud = require('wx-server-sdk')
//json2csv
// const {
//   Parser
// } = require('json2csv')

//node-xlsx
const xlsx = require('node-xlsx');

//废弃的csv
// const fields = [
//   '_id',
//   'event_id',
//   'school',
//   'title',
//   'mark',
//   'nickName',
//   'voter_openid',
//   'datetime'
// ]
// const opts = {
//   fields
// }

//日期格式化
Date.prototype.Format = function(fmt) {
  var o = {
    "M+": this.getMonth() + 1,
    "d+": this.getDate(),
    "h+": this.getHours(),
    "m+": this.getMinutes(),
    "s+": this.getSeconds(),
    "q+": Math.floor((this.getMonth() + 3) / 3),
    "S": this.getMilliseconds()
  };
  if (/(y+)/.test(fmt))
    fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
  for (var k in o)
    if (new RegExp("(" + k + ")").test(fmt))
      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
  return fmt;
}

cloud.init()
const db = cloud.database()
const MAX_LIMIT = 100

//引入发送邮件的类库
var nodemailer = require('nodemailer')
// 创建一个SMTP客户端配置
var config = {
  host: 'smtp.163.com', //网易163邮箱 smtp.163.com
  port: 465, //网易邮箱端口 25
  auth: {
    user: 'xxxx@163.com', //邮箱账号
    pass: 'xxxxx' //邮箱的授权码
  }
}
// 创建一个SMTP客户端对象
var transporter = nodemailer.createTransport(config)
// 云函数入口函数
exports.main = async(event, context) => {
  var email_address = event.email_address

  //取集合所有数据,突破一次100条的限制,需要自己改环境
  const countResult = await db.collection('eventVote').count()
  const total = countResult.total
  // 计算需分几次取
  const batchTimes = Math.ceil(total / 100)
  // 承载所有读操作的 promise 的数组
  const tasks = []
  for (let i = 0; i < batchTimes; i++) {
    const promise = db.collection('eventVote').skip(i * MAX_LIMIT).limit(MAX_LIMIT).get()
    tasks.push(promise)
  }
  // 等待所有
  var myData = (await Promise.all(tasks)).reduce((acc, cur) => {
    return {
      data: acc.data.concat(cur.data),
      errMsg: acc.errMsg,
    }
  })

  // 一次只能取100条数据,对于更多的数据集合需要用上面的方法
  // try {
  //   var myData = await db
  //     .collection('eventVote')
  //     .orderBy('datetime', 'desc')
  //     .orderBy('event_id', 'desc')
  //     .orderBy('school', 'desc')
  //     .orderBy('title', 'desc')
  //     .get()
  //   //console.log(JSON.stringify(myData))
  // } catch (err) {
  //   console.error(err)
  // }

  try {
    let userdata = myData.data
    let d = new Date().Format("yyyyMMddhhmmss")
    //1,定义excel表格名
    let dataCVS = 'excel/result' + d + '.xlsx'
    //2,定义存储数据的
    let alldata = [];
    let row = [
      '_id唯一序号',
      'event_id活动id',
      'school学院',
      'title标题',
      'mark评委打分',
      'nickName评委微信昵称',
      'voter_openid评委微信唯一id',
      'datetime提交时间'
    ]; //表头
    alldata.push(row);
    for (let key in userdata) {
      let arr = [];
      arr.push(userdata[key]._id);
      arr.push(userdata[key].event_id);
      arr.push(userdata[key].school);
      arr.push(userdata[key].title);
      arr.push(userdata[key].mark);
      arr.push(userdata[key].nickName);
      arr.push(userdata[key].voter_openid);
      arr.push(userdata[key].datetime);
      alldata.push(arr)
    }
    //console.log(alldata)
    //3,把数据保存到excel里
    var buffer = await xlsx.build([{
      name: "投票数据",
      data: alldata
    }]);
    //4,把excel文件保存到云存储里
    var fileres = await cloud.uploadFile({
      cloudPath: dataCVS,
      fileContent: buffer, //excel二进制文件
    })
  } catch (e) {
    console.error(e)
    return e
  }
  //console.log(fileres)
  //获取真实的下载地址
  const fileUrl = [fileres.fileID]
  const result = await cloud.getTempFileURL({
    fileList: fileUrl,
  })
  var downloadUrl = result.fileList[0].tempFileURL
  console.log(result.fileList)

  //废弃的csv
  // const json2csvParser = new Parser({
  //   fields
  // })
  // const csv = await json2csvParser.parse(myData.data)
  // console.log(myData)
  // console.log(myData.data)
  // console.log(csv)
  // 创建一个邮件对象
  var htmltext =
    '<p>您好,您的投票数据已经导出</p> <p>请点击下方链接下载excel文件</p> <p>字段说明:</p> <figure><table><thead><tr><th>字段名称</th><th>字段说明</th></tr></thead><tbody><tr><td>_id</td><td>本投票唯一编号</td></tr><tr><td>event_id</td><td>活动编号</td></tr><tr><td>school</td><td>学院名</td></tr><tr><td>title</td><td>团队演出标题</td></tr><tr><td>mark</td><td>投票的打分</td></tr><tr><td>nickName</td><td>提交者的微信昵称</td></tr><tr><td>voter_openid</td><td>提交者微信唯一识别号</td></tr><tr><td>datetime</td><td>提交者提交投票时间</td></tr></tbody></table></figure> <p>投票数据(请点击下载):</p>' +
    downloadUrl
  console.log(htmltext)
  var mail = {
    // 发件人
    from: '新英投票系统 <xxxxx@163.com>',
    // 主题
    subject: '投票评分数据',
    // 收件人
    to: email_address,
    // 邮件内容,text或者html格式
    html: htmltext //可以是链接,也可以是验证码
  }

   let res = await transporter.sendMail(mail)
   return res
}

具体的话,更改邮件的地址密码,更改需要的html和数据库环境id即可使用,上传到云环境即可。