go 生成图片,邮件发送问题

一、问题背景:

	每天发送各个部门的财报到邮件.
	由于部门较多,且数据量较大,导致单纯的表格模式数字量太多.
	影响观看,且不易发现问题,分析数据。

二、项目初审,项目技术选型

在这里插入图片描述

1、后端做定时任务调度
2、后端提供明细数据接口
3、前端做数据页面展示
4、使用PhantomJS 实现网页截图功能
5、图片以对象存储的形势保存
6、定时任务推送数据

三、项目涉及的技术解决方案

1、PhantomJS

官网链接:https://www.cnblogs.com/bangejingting/p/6907628.html
1.1、phantomjs的安装
  • 安装目录放在data目录下
    $ cd /data/

  • 远程下载
    $ wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2

  • 解压tar.bz2文件
    $ tar -jxvf phantomjs-2.1.1-linux-x86_64.tar.bz2
    $ cd phantomjs-2.1.1-linux-x86_64/bin/

  • 对可执行文件进行web用户授权
    $ chown -R web:web bin/

  • 将该脚本命令作为全局脚本
    $ cp phantomjs /bin/

1.2、phantomjs的使用
  • 调用脚本 参数为执行的js脚本文件
	$ phantomjs  --web-security=no xxx.js htmlPath imgPath

说明:
	--web-security=no  // 解决跨域问题
	 xxx.js  // 需要执行的脚本
	 htmlPath  // 需要生成的网页地址, 可以是绝对路径下的html  /data/html/index.html
	imgPath // 生成的图片地址及图片名称  /data/img/xxx.jpg  
  • xxx.js样例:
var page = require('webpage').create(),
    system = require('system'),
    address, output, size;
// 设置整体渲染页面的大小,非必需设置
// page.viewportSize = {width: 800, height: 400};
address = system.args[1];
output = system.args[2];
// 打开的html文件,可以是相对地址
page.open(address, function (status) {
    page.render(output);
    page.close();
    phantom.exit(1);
    console.log('render ok');
});

说明:
	address:需要截图的地址
	output: 输出的图片
phantomjs的注意事项
  • 不支持es6语法,只能使用es5

2、对象存储文件上传

  • 单文件上传
// 单文件上传
func CloudUploadFile(f *multipart.FileHeader, fileName string) error {
	appID := AppID
	secretID := SecretID
	secretKey :=SecretKey
	u, _ := url.Parse("http://"+bucketName+"-"+appID+".cos.ap-xianggang.myqcloud.com")

	client := libcos.NewClient1(u, appID, secretID, secretKey, 10)

	// 读取文件内容
	fileContent, err := f.Open()
	if err != nil {
		fmt.Sprintf("err:%v\n",e.Error())
		return err
	}
	fi, err := ioutil.ReadAll(fileContent)
	if err != nil {
		fmt.Sprintf("err:%v\n",e.Error())
		return err
	}
	fReader := bytes.NewReader(fi)

	// 上传
	err = client.Put(context.Background(), fileName, fReader)
	if err != nil {
		fmt.Sprintf("err:%v\n",e.Error())
		return err
	}
	return nil
	}
  • 文件夹上传
//获取指定目录下的所有文件,包含子目录下的文件
func GetAllFiles(dirPth string) (files []string, err error) {
	var dirs []string
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		return nil, err
	}
	PthSep := string(os.PathSeparator)
	//suffix = strings.ToUpper(suffix) //忽略后缀匹配的大小写
	for _, fi := range dir {
		if fi.IsDir() { // 目录, 递归遍历
			dirs = append(dirs, dirPth+PthSep+fi.Name())
			GetAllFiles(dirPth + PthSep + fi.Name())
		} else {
			files = append(files, dirPth+PthSep+fi.Name())
		}
	}
	// 读取子目录下文件
	for _, table := range dirs {
		temp, _ := GetAllFiles(table)
		for _, temp1 := range temp {
			files = append(files, temp1)
		}
	}
	return files, nil
}

// 文件逐个上传
func DownloadFile(c *gin.Context, filePath string) error {
	// 获取所有文件 
	files, err := GetAllFiles(filePath)
	if err != nil {
		fmt.Sprintf("err:%v\n", e.Error())
		return err
	}
	start := time.Now().Unix()
	var wg sync.WaitGroup
	con := make(chan int, 30)
	// 遍历文件 逐个上传
	for _, v := range files {
		wg.Add(1)
		go func(v string) {
			con <- 1
			defer func() {
				<-con
				wg.Done()
			}()
			file, _ := os.Open(v)
			defer file.Close()
			stats, _ := file.Stat()
			fileByte := make([]byte, stats.Size())
			file.Read(fileByte)

			// 取每一个文件的实际地址  /home/web/game/1.txt  => 实际目录 /game/1.txt
			filepath := strings.Replace(v, "/home/web", "", 1)
			err = FileUploading(c, fileByte, filepath)
			if err != nil {
				fmt.Sprintf("err:%v\n", e.Error())
			}
		}(v)
	}
	wg.Wait()
	return err
}

//  上传文件夹到腾讯云 对象存储
func FileUploading(fileByte []byte, fileName string) error {
	bucketName := BucketName
	appID := AppID
	secretID := SecretID
	secretKey := SecretKey
	u, _ := url.Parse("http://" + bucketName + "-" + appID + ".cos.ap-xiangguang.myqcloud.com")
	client := libcos.NewClient1(u, appID, secretID, secretKey, 10)
	fReader := bytes.NewReader(fileByte)
	// 上传
	err := client.Put(context.Background(), fileName, fReader)
	if err != nil {
		fmt.Sprintf("err:%v\n", e.Error())
		return err
	}
	return nil
}

3、邮件发送

3.1、解决邮件文字显示乱码

$ yum install -y fontconfig mkfontscale
$ fc-list :lang=zh

$ mkdir -p /usr/share/fonts/zh_CN

# 提前复制平方、微软雅黑到服务器
$ cp /tmp/PingFang.ttc .
$ cp /tmp/msyh.ttf .
$ chmod -R 766 ../zh_CN/

$ mkfontscale
$ mkfontdir
3.2、发送邮件

邮箱配置请添加图片描述

3.3、发送邮件
func SendEmail() error {
	m := gomail.NewMessage()
	m.SetHeader("From", xxx)        //发送人
	m.SetHeader("To",[]string{xxx,xxx})      //接收人 
	m.SetHeader("Subject",xxx ) //邮件主题 string
	m.SetBody("text/html", xxxx)      //正文 string
	// 发邮件地址 smtp.exmail.qq.com qq邮箱, port 邮箱设置里可以查到, 发送者邮箱名称, 邮箱密钥, 上一步可以查到,不是邮箱登录密码
	d := gomail.NewDialer(FromEmailAddr, FromEmailPort, FromEmailName, FromEmailPass)
	if err := d.DialAndSend(m); err != nil {
		return fmt.Errorf("err:%v\n", err.Error())
	}
	return nil
}