go-chart 发送图文的统计报表邮件

原文:https://blog.csdn.net/zangdaiyang1991/article/details/105200062

----------------------------------------------------------------

需求

邮件中实现统计信息的发送。统计信息以折线图、饼图、柱状图的形式展示。因为邮件中无法支持js解析,所以采用go-chart生成PNG图片后base64编码后发送。

<img src="data:image/png;base64,xxx">

1

go-chart实战

go-chart是个强大的go生成图片的库,但是采用默认配置生成线条较多或者文本过长的图片时,无法完美适应。默认不支持中文。但是里面的属性大多可自己定义,整体来说非常强大。

代码

package charter

import (

"bytes"

"encoding/base64"

"fmt"

"math/rand"

"os"

"time"

"github.com/wcharczuk/go-chart"

"github.com/wcharczuk/go-chart/drawing"

)

const (

lineChartXAxisName = "Date"

lineChartYAxisName = "Count"

lineChartHeight = 700

lineChartWidth = 1280

colorMultiplier = 256

imgStrPrefix = "data:image/png;base64,"

pieLabelFormat = "%v %v"

barChartTryAgainErr = "invalid data range; cannot be zero"

)

var (

lineChartStyle = chart.Style{

Padding: chart.Box{

Top: 30,

Left: 150,

},

}

defaultChartStyle = chart.Style{

Padding: chart.Box{

Top: 30,

},

}

timeFormat = chart.TimeDateValueFormatter

)

type LineYValue struct {

Name string

Values []float64

}

type ChartValue struct {

Name string

Value float64

}

// createLineChart 创建线性图

func createLineChart(title string, endTime time.Time, values []LineYValue) (img string, err error) {

if len(values) == 0 {

return

}

// 1、计算X轴

lenX := len(values[0].Values)

// X轴内容xValues 及 X轴坐标ticks

var xValues []time.Time

var ticks []chart.Tick

for i := lenX - 1; i >= 0; i-- {

curTime := endTime.AddDate(0, 0, -i)

xValues = append(xValues, curTime)

ticks = append(ticks, chart.Tick{Value: getNsec(curTime), Label: timeFormat(curTime)})

}

// 2、生成Series

var series []chart.Series

for _, yValue := range values {

series = append(series, chart.TimeSeries{

Name: yValue.Name,

Style: chart.Style{

// 随机渲染线条颜色

StrokeColor: drawing.Color{

R: uint8(rand.Intn(colorMultiplier)),

G: uint8(rand.Intn(colorMultiplier)),

B: uint8(rand.Intn(colorMultiplier)),

A: uint8(colorMultiplier - 1), // 透明度

},

},

XValues: xValues,

YValues: yValue.Values,

})

}

// 3、新建图形

graph := chart.Chart{

Title: title,

Background: lineChartStyle,

Width: lineChartWidth,

Height: lineChartHeight,

XAxis: chart.XAxis{

Name: lineChartXAxisName,

ValueFormatter: timeFormat,

Ticks: ticks,

},

YAxis: chart.YAxis{

Name: lineChartYAxisName,

},

Series: series,

}

graph.Elements = []chart.Renderable{

chart.LegendLeft(&graph),

}

// 4、输出目标

img, err = writeLineChart(&graph)

return

}

// getNsec 获取纳秒数

func getNsec(cur time.Time) float64 {

return float64(cur.Unix() * int64(time.Second))

}

func writeLineChartToPng(c *chart.Chart) (img string, err error) {

f, _ := os.Create("graph.png")

err = c.Render(chart.PNG, f)

return

}

func writeLineChart(c *chart.Chart) (img string, err error) {

var imgContent bytes.Buffer

err = c.Render(chart.PNG, &imgContent)

if err != nil {

return

}

img = imgToStr(imgContent)

return

}

func imgToStr(imgContent bytes.Buffer) string {

return imgStrPrefix + base64.StdEncoding.EncodeToString(imgContent.Bytes())

}

// createPieChart 创建饼图

func createPieChart(title string, pieValues []ChartValue) (img string, err error) {

if len(pieValues) == 0 {

return

}

// 1、构建value

var values []chart.Value

for _, v := range pieValues {

values = append(values, chart.Value{

Value: v.Value,

Label: fmt.Sprintf(pieLabelFormat, getSimpleSensType(v.Name), formatValue(v.Value)),

})

}

// 2、新建饼图

pie := chart.PieChart{

Title: title,

Background: defaultChartStyle,

Values: values,

}

// 4、输出目标

img, err = writePieChart(&pie)

return

}

func formatValue(f float64) string {

return fmt.Sprintf("%.2fW", f/10000)

}

func writePieChartToPng(c *chart.PieChart) (img string, err error) {

f, _ := os.Create("pie.png")

err = c.Render(chart.PNG, f)

return

}

func writePieChart(c *chart.PieChart) (img string, err error) {

var imgContent bytes.Buffer

err = c.Render(chart.PNG, &imgContent)

if err != nil {

return

}

img = imgToStr(imgContent)

return

}

// createBarChart 创建柱状图

func createBarChart(title string, barValues []ChartValue) (img string, err error) {

if len(barValues) == 0 {

return

}

// 1、构建value

var values []chart.Value

for _, v := range barValues {

values = append(values, chart.Value{

Value: v.Value,

Label: v.Name,

})

}

// 2、新建饼图

bar := chart.BarChart{

XAxis: chart.Style{

TextWrap: 0, // default 1为可以溢出规定的范围

},

Width: 2560,

BarWidth: 50,

BarSpacing: 300,

Title: title,

Background: defaultChartStyle,

Bars: values,

}

// 4、输出目标

img, err = writeBarChart(&bar)

if err != nil && err.Error() == barChartTryAgainErr {

// 添加一个隐藏条目,设置透明度A为0, 设置任意属性如R不为0即可

values = append(values, chart.Value{

Style: chart.Style{

StrokeColor: drawing.Color{R: 1},

},

Value: 0,

Label: "",

})

bar.Bars = values

img, err = writeBarChart(&bar)

}

return

}

func writeBarChartToPng(c *chart.BarChart) (img string, err error) {

f, _ := os.Create("bar.png")

err = c.Render(chart.PNG, f)

return

}

func writeBarChart(c *chart.BarChart) (img string, err error) {

var imgContent bytes.Buffer

err = c.Render(chart.PNG, &imgContent)

if err != nil {

return

}

img = imgToStr(imgContent)

return

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

测试代码和生成图片效果

修改以上writeXxxChart为writeXxxChartToPng后,运行以下测试代码

package charter

import (

"testing"

"time"

"github.com/stretchr/testify/assert"

)

func TestCreateLineChart(t *testing.T) {

testAssert := assert.New(t)

tests := []struct {

title string

endTime time.Time

barValues []LineYValue

}{

{"line chart", time.Now(), []LineYValue{

{"asd", []float64{1, 2, 300, 100, 200, 6, 700}},

{"hgj", []float64{400, 500000, 200, 50, 5, 800, 7}},

{"dfg45r", []float64{1, 2, 700, 100, 200, 6, 700}},

{"2342sr", []float64{400, 500000, 200, 50, 5, 800, 7}},

{"das21-asd", []float64{300000, 200000, 400000, 100000, 400000, 450000, 400000}},

{"csc", []float64{400, 500000, 200, 50, 5, 800, 7}},

{"mhj", []float64{1, 2, 300, 100, 200, 6, 700}},

{"876ijgh", []float64{400, 500000, 200, 50, 5, 800, 7}},

{"fbfdv", []float64{1, 2, 300, 100, 200, 6, 700}},

{"67ds", []float64{400, 10000, 200, 50, 5, 800, 7}},

{"67bdfv", []float64{1, 2, 300, 100, 200, 6, 700}},

{"sdf324", []float64{400, 500000, 200, 50, 5, 800, 7}},

{"vdf67", []float64{1, 2, 300, 100, 200, 6, 700}},

{"vdfs234", []float64{400, 500000, 200, 50, 5, 800, 7}},

{"123sdf", []float64{1, 2, 700, 100, 200, 6, 700}},

{"aasdasd", []float64{400, 500000, 200, 50, 5, 800, 7}},

{"aasd", []float64{1, 2, 300, 100, 200, 6, 700}},

{"basd", []float64{400, 500000, 200, 50, 5, 800, 7}},

{"cczx", []float64{1, 2, 300, 100, 200, 6, 700}},

{"qweqw", []float64{400, 500000, 200, 50, 5, 800, 7}},

{"asdadf", []float64{1, 2, 300, 100, 200, 6, 700}},

{"fghfh", []float64{400, 500000, 200, 50, 5, 800, 7}},

{"erttyrt", []float64{1, 2, 300, 100, 200, 6, 700}}}},

}

for _, test := range tests {

img, err := createLineChart(test.title, test.endTime, test.barValues)

testAssert.Equal(img, "")

testAssert.Equal(err, nil)

}

}

func TestCreatePieChart(t *testing.T) {

testAssert := assert.New(t)

tests := []struct {

title string

pieValues []ChartValue

}{

{"pie chart", []ChartValue{{"asdas", 20000}, {"q12asd", 300000}, {"ascasd", 3000}}},

}

for _, test := range tests {

img, err := createPieChart(test.title, test.pieValues)

testAssert.Equal(img, "")

testAssert.Equal(err, nil)

}

}

func TestCreateBarChart(t *testing.T) {

testAssert := assert.New(t)

tests := []struct {

title string

pieValues []ChartValue

}{

{"bar chart", []ChartValue{{"asdascasd\nasd-asd", 20}, {"asdascascasdasdasd.go\nasdasd-asdasd", 30}, {"asasdasd.asdasd]\nasdasd-asda", 100},

{"asdasdasda.go\nasdasd-asdasd", 20}, {"asdasd.asdasd\ngeass", 30}, {"asdasdasd\nasdasd-asdasd", 100},

{"asdasd_adsdasd_dasd\asd-asd", 20}, {"asdascasdcad\nasdasdasda", 30}, {"asdasdasdasd", 100},

{"asdasclkhy9p867p9", 20}}},

}

for _, test := range tests {

img, err := createBarChart(test.title, test.pieValues)

testAssert.Equal(img, "")

testAssert.Equal(err, nil)

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

相关:

https://github.com/wcharczuk/go-chart/issues/62

go-chart使用中文

go-echarts: html格式图表

————————————————

版权声明:本文为CSDN博主「藏呆羊」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/zangdaiyang1991/java/article/details/105200062