github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/helper/helper.go (about) 1 package helper 2 3 /* 4 helper 模块是纯功能性质 辅助性质的代码 5 对数据库直接操作的一切代码都不能写在此 6 */ 7 8 import ( 9 "bufio" 10 "bytes" 11 "crypto/aes" 12 "crypto/cipher" 13 "crypto/md5" 14 crand "crypto/rand" 15 "crypto/rsa" 16 "crypto/sha1" 17 "crypto/x509" 18 "encoding/base64" 19 "encoding/hex" 20 "encoding/pem" 21 "errors" 22 "fmt" 23 "html/template" 24 "image" 25 "image/gif" 26 "image/jpeg" 27 "image/png" 28 "io" 29 "io/ioutil" 30 "log" 31 "math" 32 "math/rand" 33 "mime/multipart" 34 "net/http" 35 "net/http/cookiejar" 36 "os" 37 "os/exec" 38 fpath "path" 39 "regexp" 40 "runtime" 41 "sort" 42 "strconv" 43 "strings" 44 "time" 45 "unicode/utf16" 46 "github.com/insionng/yougam/libraries/bluemonday" 47 "github.com/insionng/yougam/libraries/gift" 48 "github.com/insionng/yougam/libraries/russross/blackfriday" 49 ) 50 51 //PrintError 打印错误 52 func PrintError() { 53 if err := recover(); err != nil { 54 log.Printf("%v", err) 55 for i := 0; i < 10; i++ { 56 funcName, file, line, ok := runtime.Caller(i) 57 if ok { 58 log.Printf("frame %v:[func:%v,file:%v,line:%v]\n", i, runtime.FuncForPC(funcName).Name(), file, line) 59 } 60 } 61 } 62 } 63 64 //FileMTime 返回文件的修改时间戳 65 func FileMTime(file string) (int64, error) { 66 f, e := os.Stat(file) 67 if e != nil { 68 return 0, e 69 } 70 return f.ModTime().Unix(), nil 71 } 72 73 //FileSize 获取文件尺寸 74 func FileSize(file string) (int64, error) { 75 f, e := os.Stat(file) 76 if e != nil { 77 return 0, e 78 } 79 return f.Size(), nil 80 } 81 82 //Unlink 删除文件 83 func Unlink(file string) error { 84 return os.Remove(file) 85 } 86 87 //Rename 重命名文件 88 func Rename(file string, to string) error { 89 return os.Rename(file, to) 90 } 91 92 //FilePutContent 将指定内容写入指定文件 93 func FilePutContent(file string, content string) (int, error) { 94 fs, e := os.Create(file) 95 if e != nil { 96 return 0, e 97 } 98 defer fs.Close() 99 return fs.WriteString(content) 100 } 101 102 /* 103 //FilePutContent 追加模式 104 func FilePutContent(path string, content string) (int, error) { 105 fs, e := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) 106 if e != nil { 107 return 0, e 108 } 109 defer fs.Close() 110 return fs.WriteString(content) 111 } 112 */ 113 114 //FileGetContent 获取指定文件的内容 115 func FileGetContent(file string) (string, error) { 116 if !IsFile(file) { 117 return "", os.ErrNotExist 118 } 119 b, e := ioutil.ReadFile(file) 120 if e != nil { 121 return "", e 122 } 123 return string(b), nil 124 } 125 126 //IsFile 当为目录或文件不存在时返回假 127 func IsFile(file string) bool { 128 f, e := os.Stat(file) 129 if e != nil { 130 return false 131 } 132 return !f.IsDir() 133 } 134 135 //IsExist 当文件或目录存在时返回真 136 func IsExist(path string) bool { 137 _, err := os.Stat(path) 138 return err == nil || os.IsExist(err) 139 } 140 141 //CreateFile 创建文件 142 func CreateFile(dir string, name string) (string, error) { 143 src := dir + name + "/" 144 if IsExist(src) { 145 return src, nil 146 } 147 148 if err := os.MkdirAll(src, 0777); err != nil { 149 if os.IsPermission(err) { 150 fmt.Println("你不够权限创建文件") 151 } 152 return "", err 153 } 154 155 return src, nil 156 } 157 158 //FileRepos file operations 159 type FileRepos []repository 160 161 type repository struct { 162 Name string 163 FileTime int64 164 } 165 166 func (r FileRepos) Len() int { 167 return len(r) 168 } 169 170 func (r FileRepos) Less(i, j int) bool { 171 return r[i].FileTime < r[j].FileTime 172 } 173 174 func (r FileRepos) Swap(i, j int) { 175 r[i], r[j] = r[j], r[i] 176 } 177 178 // DelFile : 获取所有文件,如果文件达到最上限,按时间删除 179 func DelFile(files []os.FileInfo, count int, fileDir string) { 180 if len(files) <= count { 181 return 182 } 183 184 result := new(FileRepos) 185 186 for _, file := range files { 187 if file.IsDir() { 188 continue 189 } else { 190 *result = append(*result, repository{Name: file.Name(), FileTime: file.ModTime().Unix()}) 191 } 192 } 193 194 sort.Sort(result) 195 deleteNum := len(files) - count 196 for k, v := range *result { 197 if k+1 > deleteNum { 198 break 199 } 200 Unlink(fileDir + v.Name) 201 } 202 203 return 204 } 205 206 // CopyFile : copy the source ro dest 207 func CopyFile(source string, dest string) (err error) { 208 sourcefile, err := os.Open(source) 209 if err != nil { 210 return err 211 } 212 213 defer sourcefile.Close() 214 215 destfile, err := os.Create(dest) 216 if err != nil { 217 return err 218 } 219 220 defer destfile.Close() 221 222 _, err = io.Copy(destfile, sourcefile) 223 if err == nil { 224 sourceinfo, err := os.Stat(source) 225 if err != nil { 226 err = os.Chmod(dest, sourceinfo.Mode()) 227 } 228 229 } 230 231 return 232 } 233 234 // CopyDir : copy source directorie to dest 235 func CopyDir(source string, dest string) (err error) { 236 237 // get properties of source dir 238 sourceinfo, err := os.Stat(source) 239 if err != nil { 240 return err 241 } 242 243 // create dest dir 244 245 err = os.MkdirAll(dest, sourceinfo.Mode()) 246 if err != nil { 247 return err 248 } 249 250 directory, _ := os.Open(source) 251 252 objects, err := directory.Readdir(-1) 253 254 for _, obj := range objects { 255 256 sourcefilepointer := source + "/" + obj.Name() 257 258 destinationfilepointer := dest + "/" + obj.Name() 259 260 if obj.IsDir() { 261 // create sub-directories - recursively 262 err = CopyDir(sourcefilepointer, destinationfilepointer) 263 if err != nil { 264 fmt.Println(err) 265 } 266 } else { 267 // perform copy 268 err = CopyFile(sourcefilepointer, destinationfilepointer) 269 if err != nil { 270 fmt.Println(err) 271 } 272 } 273 274 } 275 return 276 } 277 278 // TouchFile : create file of blank 279 func TouchFile(path string) { 280 if !IsExist(path) { 281 WriteFile(path, "") 282 } 283 } 284 285 // Pages : 分页计算函数 286 func Pages(totalRecords int64, page int64, pageSize int64) (pages int64, pageOutput int64, beginNum int64, endNum int64, offset int64) { 287 //取得记录总数,计算总页数用 288 //totalRecords,总共有totalRecords条记录 289 290 //设定每一页显示的记录数 291 if pageSize < 0 || pageSize < 1 { 292 pageSize = 10 //如无设置,则默认每页显示10条记录 293 } 294 295 //计算总页数 结果逢余进一取整 296 //pages = int64(math.Ceil(float64(totalRecords / pageSize))) 297 pages = (totalRecords + pageSize - 1) / pageSize 298 //返回pages 299 300 //判断页数设置,否则,设置为第一页 301 if page < 0 || page < 1 { 302 page = 1 303 } 304 if page > pages { 305 page = pages 306 } 307 //返回page 308 309 beginNum = page - 4 310 endNum = page + 5 311 312 if page < 5 { 313 beginNum = 1 314 endNum = 10 //可用链接数,现在是当前页加前后两页共5页,if条件为可用链接数的一半 315 } 316 if page > pages-5 { 317 beginNum = pages - 9 318 endNum = pages 319 } 320 if beginNum < 1 { 321 beginNum = 1 322 } 323 if endNum > pages { 324 endNum = pages 325 } 326 //返回beginNum 327 //返回endNum 328 329 //计算记录偏移量 330 offset = (page - 1) * pageSize 331 return pages, page, beginNum, endNum, offset 332 } 333 334 // Pagesbar : is for the html template 335 func Pagesbar(url string, keyword string, resultsMax int64, pages int64, page int64, beginNum int64, endNum int64, style int64) (output template.HTML) { 336 var raw string 337 switch { 338 case style == 0: //sdc 定制版pagesbar 339 340 if keyword != "" { 341 keyword = keyword + "/" 342 } 343 nextpage, prevpage := int64(0), int64(0) 344 pagemindle := 2 345 pagewidth := pagemindle * 2 346 raw = `<div class="page-nav"><ul class="pagination">` 347 if resultsMax > 0 { 348 count := pages + 1 349 //prev page 350 if (page != beginNum) && (page > beginNum) { 351 prevpage = page - 1 352 raw = raw + `<li><a class="prev" href="` + url + keyword + "page" + strconv.FormatInt(prevpage, 10) + `/">上一页</a></li>` 353 } 354 355 //current page and loop pages 356 j := 0 357 for i := page; i < count; i++ { 358 j++ 359 if i == page { 360 raw = raw + `<li class="active"><a href="` + url + keyword + "page" + strconv.FormatInt(i, 10) + `/">` + strconv.FormatInt(i, 10) + "</a></li>" 361 } else { 362 363 raw = raw + `<li><a href="` + url + keyword + "page" + strconv.FormatInt(i, 10) + `/">` + strconv.FormatInt(i, 10) + "</a></li>" 364 } 365 if j > pagewidth { 366 break 367 } 368 } 369 370 raw = raw + "<li><span>共" + strconv.FormatInt(pages, 10) + "页</span></li>" 371 372 //next page 373 if (page != endNum) && (page < endNum) { 374 nextpage = page + 1 375 raw = raw + `<li><a class="next" href="` + url + keyword + "page" + strconv.FormatInt(nextpage, 10) + `/">下一页</a></li>` 376 } 377 } else { 378 raw = raw + "<li><span>共0页</span></li>" 379 380 } 381 raw = raw + "</ul></div>" 382 /* 383 if nextpage == 0 && prevpage == 0 { 384 output = template.HTML(raw) 385 } else { 386 387 if nextpage > 0 || prevpage > 0 { 388 raw = raw + `<div id="pagenavi-fixed">` 389 390 if prevpage > 0 { 391 raw = raw + ` 392 <div class="pages-prev"> 393 <a href="` + url + keyword + `page` + strconv.FormatInt(prevpage, 10) + `/">上一页 »</a> 394 </div>` 395 } 396 397 if nextpage > 0 { 398 raw = raw + ` 399 <div class="pages-next"> 400 <a href="` + url + keyword + `page` + strconv.Itoa(nextpage) + `/">下一页 »</a> 401 </div>` 402 } 403 raw = raw + "</div>" 404 } 405 output = template.HTML(raw) 406 } 407 */ 408 output = template.HTML(raw) 409 case style == 1: 410 /* 411 <ul class="pager"> 412 <li class="previous"> 413 <a href="#">← Older</a> 414 </li> 415 <li class="next"> 416 <a href="#">Newer →</a> 417 </li> 418 </ul> 419 */ 420 if resultsMax > 0 { 421 raw = "<ul class='pager'>" 422 count := pages + 1 423 //begin page 424 if (page != beginNum) && (page > beginNum) { 425 raw = raw + "<li class='previous'><a href='?" + keyword + "page=" + strconv.FormatInt((page-1), 10) + "'>«</a></li>" 426 } 427 428 for i := int64(1); i < count; i++ { 429 //current page and loop pages 430 if i == page { 431 raw = raw + "<li class='active'><a href='javascript:void();'>" + strconv.FormatInt(i, 10) + "</a></li>" 432 } else { 433 raw = raw + "<li><a href='?" + keyword + "page=" + strconv.FormatInt(i, 10) + "'>" + strconv.FormatInt(i, 10) + "</a></li>" 434 } 435 } 436 437 //next page 438 if (page != endNum) && (page < endNum) { 439 raw = raw + "<li class='next'><a href='?" + keyword + "page=" + strconv.FormatInt((page+1), 10) + "'>»</a></li>" 440 } 441 raw = raw + "</ul>" 442 } 443 444 output = template.HTML(raw) 445 case style == 2: 446 /* 447 <div class="pagination"><ul> 448 <li><a href="#">«</a></li> 449 <li class="active"><a href="#">1</a></li> 450 <li><a href="#">2</a></li> 451 <li><a href="#">3</a></li> 452 <li><a href="#">4</a></li> 453 <li><a href="#">»</a></li> 454 </ul></div> 455 */ 456 457 if resultsMax > 0 { 458 raw = "<div class='pagination pagination-centered'><ul>" 459 count := pages + 1 460 //begin page 461 if (page != beginNum) && (page > beginNum) { 462 raw = raw + "<li><a href='?" + keyword + "page=" + strconv.FormatInt((page-1), 10) + "'>«</a></li>" 463 } 464 for i := int64(1); i < count; i++ { 465 //current page and loop pages 466 if i == page { 467 raw = raw + "<li class='active'><a href='javascript:void();'>" + strconv.FormatInt(i, 10) + "</a></li>" 468 } else { 469 raw = raw + "<li><a href='?" + keyword + "page=" + strconv.FormatInt(i, 10) + "'>" + strconv.FormatInt(i, 10) + "</a></li>" 470 } 471 //next page 472 if (page != endNum) && (page < endNum) && (i == pages) { 473 raw = raw + "<li><a href='?" + keyword + "page=" + strconv.FormatInt((page+1), 10) + "'>»</a></li>" 474 } 475 } 476 raw = raw + "</ul></div>" 477 } 478 479 output = template.HTML(raw) 480 case style == 3: 481 /* 482 <div class="pagenav"> 483 <p> 484 <a href="" class="on">1</a> 485 <a href="">2</a> 486 <a href="">3</a> 487 <a href="">4</a> 488 </p> 489 </div> 490 */ 491 raw = "<div class=\"pagenav\">" 492 if resultsMax > 0 { 493 raw = raw + "<p>" 494 count := pages + 1 495 for i := int64(1); i < count; i++ { 496 if i == page { //当前页 497 raw = raw + "<a onclick=\"javascript:void();\" class=\"on\">" + strconv.FormatInt(i, 10) + "</a>" 498 } else { //普通页码链接 499 raw = raw + "<a href='?" + keyword + "page=" + strconv.FormatInt(i, 10) + "'>" + strconv.FormatInt(i, 10) + "</a>" 500 } 501 } 502 if (page != pages) && (page < pages) { //下一页 503 raw = raw + "<a class='next' href='?" + keyword + "page=" + strconv.FormatInt((page+1), 10) + "'>下一页</a>" 504 } 505 506 } else { 507 raw = raw + "<h2>No Data!</h2>" 508 raw = raw + "<span class='page-numbers'>共0页</span>" 509 } 510 raw = raw + "</p>" 511 output = template.HTML(raw + "</div>") 512 case style == 4: //mzr 定制版pagesbar 513 514 if keyword != "" { 515 keyword = keyword + "/" 516 } 517 nextpage, prevpage := int64(0), int64(0) 518 pagemindle := 7 519 pagewidth := pagemindle * 2 520 raw = "<div class='pagination'>" 521 if resultsMax > 0 { 522 raw = raw + "<span class='page-numbers'>共" + strconv.FormatInt(pages, 10) + "页</span>" 523 count := pages + 1 524 //prev page 525 if (page != beginNum) && (page > beginNum) { 526 prevpage = page - 1 527 raw = raw + "<a class='prev page-numbers' href='" + url + keyword + "" + strconv.FormatInt(prevpage, 10) + "/'>Prev</a>" 528 } 529 530 //current page and loop pages 531 j := 0 532 for i := page; i < count; i++ { 533 j++ 534 if i == page { 535 raw = raw + "<span class='page-numbers current'>" + strconv.FormatInt(i, 10) + "</span>" 536 } else { 537 538 raw = raw + "<a class='page-numbers' href='" + url + keyword + "" + strconv.FormatInt(i, 10) + "/'>" + strconv.FormatInt(i, 10) + "</a>" 539 } 540 if j > pagewidth { 541 break 542 } 543 } 544 545 //next page 546 if (page != endNum) && (page < endNum) { 547 nextpage = page + 1 548 raw = raw + "<a class='next page-numbers' href='" + url + keyword + "" + strconv.FormatInt(nextpage, 10) + "/'>Next</a>" 549 } 550 } else { 551 raw = raw + "<span class='page-numbers'>共0页</span>" 552 } 553 raw = raw + "</div>" 554 555 if nextpage == 0 && prevpage == 0 { 556 output = template.HTML(raw) 557 } else { 558 559 if nextpage > 0 || prevpage > 0 { 560 raw = raw + `<div id="pagenavi-fixed">` 561 562 if prevpage > 0 { 563 raw = raw + ` 564 <div class="pages-prev"> 565 <a href="` + url + keyword + `` + strconv.FormatInt(prevpage, 10) + `/" >上一页 »</a> 566 </div>` 567 } 568 569 if nextpage > 0 { 570 raw = raw + ` 571 <div class="pages-next"> 572 <a href="` + url + keyword + `` + strconv.FormatInt(nextpage, 10) + `/" >下一页 »</a> 573 </div>` 574 } 575 raw = raw + "</div>" 576 } 577 output = template.HTML(raw) 578 } 579 case style == 5: //yougam 定制版pagesbar 580 /* 581 <ul class="pager"> 582 <li class="previous disabled"> 583 <a>上一页</a> 584 </li> 585 <li class="pager-nums">1 / 11</li> 586 <li class="next"> 587 <a href="/?p=2">下一页</a> 588 </li> 589 </ul> 590 */ 591 if keyword != "" { 592 keyword = keyword + "/" 593 } 594 595 if resultsMax > 0 { 596 raw = "<ul class='pager'>" 597 //begin page 598 if (page != beginNum) && (page > beginNum) { 599 raw = raw + "<li class='previous'><a href='" + url + keyword + "page" + strconv.FormatInt((page-1), 10) + "/'>上一页</a></li>" 600 } 601 602 raw = raw + `<li class="pager-nums">` + strconv.FormatInt(page, 10) + ` / ` + strconv.FormatInt(pages, 10) + `</li>` 603 604 //next page 605 if (page != endNum) && (page < endNum) { 606 raw = raw + "<li class='next'><a href='" + url + keyword + "page" + strconv.FormatInt((page+1), 10) + "/'>下一页</a></li>" 607 } 608 raw = raw + "</ul>" 609 } 610 611 output = template.HTML(raw) 612 613 } 614 615 return output 616 } 617 618 // TimeSince : 微博时间格式化显示 619 // timestamp,标准时间戳 620 func TimeSince(timestamp int64) string { 621 622 //减去8小时 623 //d, _ := time.ParseDuration("-8h") 624 //t := timestamp.Add(d) 625 //since := int(time.Since(t).Minutes()) 626 //since := math.Abs(float64(time.Now().UTC().Unix() - timestamp)) 627 628 since := (time.Now().Unix() - timestamp) 629 output := "" 630 switch { 631 case (since < 60): 632 output = "刚刚" //"小于 1 分钟" 633 case (since < 60*60): 634 output = fmt.Sprintf("%v 分钟之前", since/(60)) 635 case (since < 60*60*24): 636 output = fmt.Sprintf("%v 小时之前", since/(60*60)) 637 case (since < 60*60*24*30): 638 output = fmt.Sprintf("%v 天之前", since/(60*60*24)) 639 case (since < 60*60*24*365): 640 output = fmt.Sprintf("%v 月之前", since/(60*60*24*30)) 641 default: 642 output = fmt.Sprintf("%v 年之前", since/(60*60*24*365)) 643 } 644 return output 645 } 646 647 // SmcTimeSince is format for time 648 func SmcTimeSince(timeAt time.Time) string { 649 now := time.Now() 650 since := math.Abs(float64(now.UTC().Unix() - timeAt.Unix())) 651 652 output := "" 653 switch { 654 case since < 60: 655 output = "刚刚" 656 case since < 60*60: 657 output = fmt.Sprintf("%v分钟前", math.Floor(since/60)) 658 case since < 60*60*24: 659 output = fmt.Sprintf("%v小时前", math.Floor(since/3600)) 660 case since < 60*60*24*2: 661 output = fmt.Sprintf("昨天%v", timeAt.Format("15:04")) 662 case since < 60*60*24*3: 663 output = fmt.Sprintf("前天%v", timeAt.Format("15:04")) 664 case timeAt.Format("2006") == now.Format("2006"): 665 output = timeAt.Format("1月2日 15:04") 666 default: 667 output = timeAt.Format("2006年1月2日 15:04") 668 } 669 // if math.Floor(since/3600) > 0 { 670 // if timeAt.Format("2006-01-02") == now.Format("2006-01-02") { 671 // output = "今天 " 672 // output += timeAt.Format("15:04") 673 // } else { 674 // if timeAt.Format("2006") == now.Format("2006") { 675 // output = timeAt.Format("1月2日 15:04") 676 // } else { 677 // output = timeAt.Format("2006年1月2日 15:04") 678 // } 679 // } 680 // } else { 681 // m := math.Floor(since / 60) 682 // if m > 0 { 683 // output = fmt.Sprintf("%v分钟前", m) 684 // } else { 685 // output = "刚刚" 686 // } 687 // } 688 return output 689 } 690 691 // ThisHour 获取这个小时的开始点 692 func ThisHour() int64 { 693 t := time.Now() 694 year, month, day := t.Date() 695 hour, _, _ := t.Clock() 696 697 return time.Date(year, month, day, hour, 0, 0, 0, time.UTC).Unix() 698 } 699 700 // ThisDate 获取今天的开始点 701 func ThisDate() int64 { 702 t := time.Now() 703 year, month, day := t.Date() 704 705 return time.Date(year, month, day, 0, 0, 0, 0, time.UTC).Unix() 706 } 707 708 //ThisWeek 获取这周的开始点 709 func ThisWeek() int64 { 710 t := time.Now() 711 year, month, day := t.AddDate(0, 0, -1*int(t.Weekday())).Date() 712 713 return time.Date(year, month, day, 0, 0, 0, 0, time.UTC).Unix() 714 } 715 716 //ThisMonth 获取这月的开始点 717 func ThisMonth() int64 { 718 t := time.Now() 719 year, month, _ := t.Date() 720 721 return time.Date(year, month, 1, 0, 0, 0, 0, time.UTC).Unix() 722 } 723 724 //ThisYear 获取今年的开始点 725 func ThisYear() int64 { 726 t := time.Now() 727 year, _, _ := t.Date() 728 729 return time.Date(year, 1, 1, 0, 0, 0, 0, time.UTC).Unix() 730 } 731 732 //FixedpathByNumber is fixed path via number 733 func FixedpathByNumber(n int, layer int) string { 734 735 hash := md5.New() 736 o := "" 737 for i := 1; i < layer+1; i++ { 738 739 s := strconv.Itoa(RangeRand(n^n/3+i) / 33) 740 hash.Write([]byte(s)) 741 result := hex.EncodeToString(hash.Sum(nil)) 742 r := result[0:n] 743 o += r + "/" 744 } 745 return o 746 } 747 748 //FixedpathByString is fixed path via string 749 func FixedpathByString(s string, layer int) string { 750 751 hash := md5.New() 752 output := "" 753 for i := 1; i < layer+1; i++ { 754 755 s += s + strconv.Itoa(i+i*i) 756 hash.Write([]byte(s)) 757 result := hex.EncodeToString(hash.Sum(nil)) 758 r := result[0:2] 759 output += r + "/" 760 } 761 return output 762 } 763 764 //StringNewRand : gen new random string 765 func StringNewRand(len int) string { 766 767 u := make([]byte, len/2) 768 769 // Reader is a global, shared instance of a cryptographically strong pseudo-random generator. 770 // On Unix-like systems, Reader reads from /dev/urandom. 771 // On Windows systems, Reader uses the CryptGenRandom API. 772 _, err := io.ReadFull(crand.Reader, u) 773 if err != nil { 774 panic(err) 775 } 776 777 return fmt.Sprintf("%x", u) 778 } 779 780 //GUID 生成36位GUID 781 func GUID() string { 782 b := make([]byte, 16) 783 784 if _, err := io.ReadFull(crand.Reader, b); err != nil { 785 return "" 786 } 787 788 return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) 789 } 790 791 //GUID32BIT generator a 32bit guid 792 func GUID32BIT() string { 793 b := make([]byte, 16) 794 795 if _, err := io.ReadFull(crand.Reader, b); err != nil { 796 return "" 797 } 798 799 return fmt.Sprintf("%x%x%x%x%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) 800 } 801 802 //StringNewUUID generates a new UUID based on version 4. 803 func StringNewUUID() string { 804 805 u := make([]byte, 16) 806 807 // Reader is a global, shared instance of a cryptographically strong pseudo-random generator. 808 // On Unix-like systems, Reader reads from /dev/urandom. 809 // On Windows systems, Reader uses the CryptGenRandom API. 810 _, err := io.ReadFull(crand.Reader, u) 811 if err != nil { 812 panic(err) 813 } 814 815 // Set version (4) and variant (2). 816 var version byte = 4 << 4 817 var variant byte = 2 << 4 818 u[6] = version | (u[6] & 15) 819 u[8] = variant | (u[8] & 15) 820 821 return fmt.Sprintf("%x-%x-%x-%x-%x", u[0:4], u[4:6], u[6:8], u[8:10], u[10:]) 822 } 823 824 //Round 函数对浮点数进行四舍五入 825 //语法 round(val,prec) 参数 val 规定要舍入的数字。 prec 规定小数点后的位数 826 func Round(val float64, prec int) float64 { 827 var t float64 828 f := math.Pow10(prec) 829 x := val * f 830 if math.IsInf(x, 0) || math.IsNaN(x) { 831 return val 832 } 833 if x >= 0.0 { 834 t = math.Ceil(x) 835 if (t - x) > 0.50000000001 { 836 t -= 1.0 837 } 838 } else { 839 t = math.Ceil(-x) 840 if (t + x) > 0.50000000001 { 841 t -= 1.0 842 } 843 t = -t 844 } 845 x = t / f 846 847 if !math.IsInf(x, 0) { 848 return x 849 } 850 851 return t 852 } 853 854 //RangeRand 生成规定范围内的整数 855 //设置起始数字范围,0开始,n截止 856 func RangeRand(n int) int { 857 858 r := rand.New(rand.NewSource(time.Now().UnixNano())) 859 return r.Intn(n) 860 861 } 862 863 //Nrand 标准正态分布随机整数,n为随机个数,从0开始 864 func Nrand(n int64) float64 { 865 //sample = NormFloat64() * desiredStdDev + desiredMean 866 // 默认位置参数(期望desiredMean)为0,尺度参数(标准差desiredStdDev)为1. 867 868 var i, sample int64 = 0, 0 869 desiredMean := 0.0 870 desiredStdDev := 100.0 871 872 r := rand.New(rand.NewSource(time.Now().UnixNano())) 873 874 for i < n { 875 rn := int64(r.NormFloat64()*desiredStdDev + desiredMean) 876 sample = rn % n 877 i++ 878 } 879 880 return math.Abs(float64(sample)) 881 } 882 883 //MD5 对字符串进行md5哈希, 884 // 返回32位小写md5结果 885 /* 886 func MD5(s string) string { 887 h := md5.New() 888 io.WriteString(h, s) 889 return fmt.Sprintf("%x", h.Sum(nil)) 890 } 891 */ 892 func MD5(s string) string { 893 hash := md5.New() 894 hash.Write([]byte(s)) 895 result := hex.EncodeToString(hash.Sum(nil)) 896 return result 897 } 898 899 //MD5to16 对字符串进行md5哈希, 900 // 返回16位小写md5结果 901 func MD5to16(s string) string { 902 return MD5(s)[8:24] 903 } 904 905 //SHA1 对字符串进行sha1哈希, 906 // 返回42位小写sha1结果 907 func SHA1(s string) string { 908 909 hasher := sha1.New() 910 hasher.Write([]byte(s)) 911 912 //result := fmt.Sprintf("%x", (hasher.Sum(nil))) 913 result := hex.EncodeToString(hasher.Sum(nil)) 914 return result 915 } 916 917 //ZeroPadding 零式补码 918 func ZeroPadding(ciphertext []byte, blockSize int) []byte { 919 padding := blockSize - len(ciphertext)%blockSize 920 padtext := bytes.Repeat([]byte{0}, padding) 921 return append(ciphertext, padtext...) 922 } 923 924 //ZeroUnPadding 零式去补码 925 func ZeroUnPadding(origData []byte) []byte { 926 length := len(origData) 927 unpadding := int(origData[length-1]) 928 return origData[:(length - unpadding)] 929 } 930 931 //PKCS5Padding PKCS5 补码 932 func PKCS5Padding(ciphertext []byte, blockSize int) []byte { 933 padding := blockSize - len(ciphertext)%blockSize 934 padtext := bytes.Repeat([]byte{byte(padding)}, padding) 935 return append(ciphertext, padtext...) 936 } 937 938 //PKCS5UnPadding PKCS5 去除补码 939 func PKCS5UnPadding(origData []byte) []byte { 940 length := len(origData) 941 // 去掉最后一个字节 unpadding 次 942 unpadding := int(origData[length-1]) 943 return origData[:(length - unpadding)] 944 } 945 946 //PKCS7Padding PKCS7补码 947 func PKCS7Padding(data []byte) []byte { 948 blockSize := 16 949 padding := blockSize - len(data)%blockSize 950 padtext := bytes.Repeat([]byte{byte(padding)}, padding) 951 return append(data, padtext...) 952 953 } 954 955 //PKCS7UnPadding 去除PKCS7补码 956 func PKCS7UnPadding(data []byte) []byte { 957 length := len(data) 958 959 // 去掉最后一个字节 unpadding 次 960 unpadding := int(data[length-1]) 961 962 if length < unpadding { 963 panic("[PKCS7UnPadding Error:Data length < unpadding]") 964 } 965 return data[:(length - unpadding)] 966 } 967 968 // PKCS7Pad pads an byte array to be a multiple of 16 969 // http://tools.ietf.org/html/rfc5652#section-6.3 970 func PKCS7Pad(data []byte) []byte { 971 dataLen := len(data) 972 blockSize := 16 973 var validLen int 974 if dataLen%blockSize == 0 { 975 validLen = dataLen 976 } else { 977 validLen = int(dataLen/blockSize+1) * blockSize 978 } 979 980 paddingLen := validLen - dataLen 981 // The length of the padding is used as the byte we will 982 // append as a pad. 983 bitCode := byte(paddingLen) 984 padding := make([]byte, paddingLen) 985 for i := 0; i < paddingLen; i++ { 986 padding[i] = bitCode 987 } 988 return append(data, padding...) 989 } 990 991 // PKCS7Unpad removes any potential PKCS7 padding added. 992 func PKCS7Unpad(data []byte) []byte { 993 dataLen := len(data) 994 blockSize := 16 995 // Edge case 996 if dataLen == 0 { 997 return nil 998 } 999 // the last byte indicates the length of the padding to remove 1000 paddingLen := int(data[dataLen-1]) 1001 1002 // padding length can only be between 1-15 1003 if paddingLen < blockSize { 1004 return data[:dataLen-paddingLen] 1005 } 1006 return data 1007 } 1008 1009 //AesCBCEncrypt aes cbc encrypt 1010 //AES-128,设key为 16 bytes. 1011 //AES-192,设key为 24 bytes. 1012 //AES-256,设key为 32 bytes. 1013 //AES256 加密CBC模式 1014 func AesCBCEncrypt(origData, key []byte) ([]byte, error) { 1015 block, err := aes.NewCipher(key) 1016 if err != nil { 1017 return nil, err 1018 } 1019 blockSize := block.BlockSize() 1020 //origData = PKCS5Padding(origData, blockSize) 1021 //origData = PKCS7Pad(origData) 1022 origData = PKCS7Padding(origData) 1023 // origData = ZeroPadding(origData, block.BlockSize()) 1024 1025 blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) 1026 crypted := make([]byte, len(origData)) 1027 // 根据CryptBlocks方法的说明,如下方式初始化crypted也可以 1028 // crypted := origData 1029 blockMode.CryptBlocks(crypted, origData) 1030 return crypted, nil 1031 } 1032 1033 //AesCBCDecrypt AES256 解密CBC模式 1034 func AesCBCDecrypt(crypted, key []byte) ([]byte, error) { 1035 block, err := aes.NewCipher(key) 1036 if err != nil { 1037 return nil, err 1038 } 1039 1040 blockSize := block.BlockSize() 1041 1042 blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) 1043 origData := make([]byte, len(crypted)) 1044 // origData := crypted 1045 blockMode.CryptBlocks(origData, crypted) 1046 //origData = PKCS5UnPadding(origData) 1047 origData = PKCS7UnPadding(origData) 1048 //origData = PKCS7Unpad(origData) 1049 // origData = ZeroUnPadding(origData) 1050 return origData, nil 1051 } 1052 1053 //AesCFBEncrypt AES256加密 CFB 1054 func AesCFBEncrypt(content string, privateKey string, publicKey string) (string, error) { 1055 1056 c, err := aes.NewCipher([]byte(privateKey)) 1057 if err != nil { 1058 //fmt.Println("AesCFBEncrypt:", err) 1059 return "", err 1060 } 1061 1062 cfb := cipher.NewCFBEncrypter(c, []byte(publicKey)) 1063 ciphertext := make([]byte, len(content)) 1064 cfb.XORKeyStream(ciphertext, []byte(content)) 1065 1066 return string(ciphertext), err 1067 1068 } 1069 1070 //AesCFBDecrypt AES256解密 CFB 1071 func AesCFBDecrypt(ciphertext string, privateKey string, publicKey string) (string, error) { 1072 c, err := aes.NewCipher([]byte(privateKey)) 1073 if err != nil { 1074 return "", err 1075 } 1076 cipherz := []byte(ciphertext) 1077 cfbdec := cipher.NewCFBDecrypter(c, []byte(publicKey)) 1078 contentCopy := make([]byte, len(cipherz)) 1079 cfbdec.XORKeyStream(contentCopy, cipherz) 1080 1081 return string(contentCopy), err 1082 } 1083 1084 //RsaEncrypt RSA加密 1085 func RsaEncrypt(origData []byte, publicKey []byte) ([]byte, error) { 1086 block, _ := pem.Decode(publicKey) 1087 if block == nil { 1088 return nil, errors.New("public key error") 1089 } 1090 pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) 1091 if err != nil { 1092 return nil, err 1093 } 1094 pub := pubInterface.(*rsa.PublicKey) 1095 return rsa.EncryptPKCS1v15(crand.Reader, pub, origData) 1096 } 1097 1098 //RsaDecrypt RSA解密 1099 func RsaDecrypt(ciphertext []byte, privateKey []byte) ([]byte, error) { 1100 block, _ := pem.Decode(privateKey) 1101 if block == nil { 1102 return nil, errors.New("Private key error!") 1103 } 1104 priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) 1105 if err != nil { 1106 return nil, err 1107 } 1108 return rsa.DecryptPKCS1v15(crand.Reader, priv, ciphertext) 1109 } 1110 1111 //Filehash get hash of file 1112 func Filehash(pathOr string, fileOr *os.File) (string, error) { 1113 1114 switch { 1115 case (pathOr != "" && fileOr == nil): 1116 1117 file, err := os.Open(pathOr) 1118 if err != nil { 1119 return "", err 1120 } 1121 defer file.Close() 1122 h := sha1.New() 1123 1124 _, erro := io.Copy(h, file) 1125 if erro != nil { 1126 return "", erro 1127 } 1128 //return fmt.Srintf("%x", h.Sum(nil)) 1129 result := hex.EncodeToString(h.Sum(nil)) 1130 //result := fmt.Sprintf("%d", h.Sum(nil)) 1131 //result, _ := fmt.Printf("%d", h.Sum(nil)) 1132 return result, nil 1133 case (pathOr == "" && fileOr != nil): 1134 h := sha1.New() 1135 _, erro := io.Copy(h, fileOr) 1136 if erro != nil { 1137 return "", erro 1138 } 1139 //return fmt.Srintf("%x", h.Sum(nil)) 1140 result := hex.EncodeToString(h.Sum(nil)) 1141 //result := fmt.Sprintf("%d", h.Sum(nil)) 1142 //result, _ := fmt.Printf("%d", h.Sum(nil)) 1143 return result, nil 1144 default: 1145 return "", errors.New("Error: 没有参数无法生成hash,请输入文件路径 或 *os.File!") 1146 } 1147 1148 } 1149 1150 //FilehashNumber 以数字形式表达文件希哈 1151 func FilehashNumber(path string) (int, error) { 1152 1153 file, err := os.Open(path) 1154 if err != nil { 1155 return 0, err 1156 } 1157 h := sha1.New() 1158 1159 _, err = io.Copy(h, file) 1160 if err != nil { 1161 return 0, err 1162 } 1163 1164 result, err := strconv.Atoi(fmt.Sprintf("%d", h.Sum(nil))) 1165 //return fmt.Srintf("%x", h.Sum(nil)) 1166 return result, err 1167 } 1168 1169 //FilehashBlock 仅以区块范围作文件哈希 1170 func FilehashBlock(path string, block int64) string { 1171 file, err := os.Open(path) 1172 defer file.Close() 1173 hash := "" 1174 1175 if err != nil { 1176 return "" 1177 } 1178 1179 data := make([]byte, block) 1180 for { 1181 n, err := file.Read(data) 1182 1183 if n != 0 { 1184 //hash = MD5(string(data)) 1185 hash = SHA1(string(data)) 1186 } else { 1187 break 1188 } 1189 1190 if err != nil && err != io.EOF { 1191 //panic(err) 1192 return "" 1193 } 1194 } 1195 1196 return hash 1197 } 1198 1199 //GetSensitiveInfoRemovedEmail 获取 sensitive 信息 1200 func GetSensitiveInfoRemovedEmail(email string) string { 1201 const ( 1202 mailSeparatorSign = "@" 1203 minMailIDLength = 2 1204 ) 1205 1206 emailSepPos := strings.Index(email, mailSeparatorSign) 1207 1208 if emailSepPos < 0 { 1209 return email 1210 } 1211 1212 mailID, mailDomain := email[:emailSepPos], email[emailSepPos+1:] 1213 1214 if mailIDLength := len(mailID); mailIDLength > minMailIDLength { 1215 firstChar, lastChar := string(mailID[0]), string(mailID[mailIDLength-1]) 1216 stars := "***" 1217 switch mailIDLength - minMailIDLength { 1218 case 1: 1219 stars = "*" 1220 case 2: 1221 stars = "**" 1222 } 1223 mailID = firstChar + stars + lastChar 1224 } 1225 1226 result := mailID + mailSeparatorSign + mailDomain 1227 return result 1228 } 1229 1230 //Substr 截取字符 1231 func Substr(strin string, start, length int, symbol string) string { 1232 rs := []rune(strin) 1233 rl := len(rs) 1234 end := 0 1235 1236 if start < 0 { 1237 start = rl - 1 + start 1238 } 1239 end = start + length 1240 1241 if start > end { 1242 start, end = end, start 1243 } 1244 1245 if start < 0 { 1246 start = 0 1247 } 1248 if start > rl { 1249 start = rl 1250 } 1251 if end < 0 { 1252 end = 0 1253 } 1254 if end > rl { 1255 end = rl 1256 } 1257 1258 rsout := string(rs[start:end]) 1259 if strin == rsout { 1260 return rsout 1261 } 1262 1263 return rsout + symbol 1264 1265 } 1266 1267 //GetFile 从因特网获取网页内容作为文件 1268 func GetFile(fileURL string, filePath string, useragent string, referer string) error { 1269 1270 f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0644) 1271 if err != nil { 1272 log.Println("os.OpenFile errors:", err) 1273 return err 1274 } 1275 stat, err := f.Stat() //获取文件状态 1276 if err != nil { 1277 log.Println("f.Stat() errors:", err) 1278 return err 1279 } 1280 1281 ss, _ := strconv.Atoi(fmt.Sprintf("%v", stat.Size)) 1282 f.Seek(int64(ss), 0) //把文件指针指到文件末 1283 1284 req, err := http.NewRequest("GET", fileURL, nil) 1285 if err != nil { 1286 log.Println("http.NewRequest errors:", err) 1287 return err 1288 } 1289 1290 if useragent == "default" { 1291 useragent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31" 1292 } 1293 1294 if referer != "" { 1295 req.Header.Set("Referer", referer) 1296 } 1297 1298 req.Header.Set("User-Agent", useragent) 1299 req.Header.Set("Range", fmt.Sprintf("bytes=%v-", stat.Size)) 1300 1301 client := &http.Client{} 1302 resp, err := client.Do(req) 1303 if err != nil { 1304 log.Println("client.Do(req) errors:", err) 1305 return err 1306 } 1307 1308 defer f.Close() 1309 defer resp.Body.Close() 1310 1311 written, err := io.Copy(f, resp.Body) 1312 if err != nil { 1313 return err 1314 } 1315 1316 fs, err := os.Stat(filePath) 1317 if err != nil { 1318 if err := os.Remove(filePath); err != nil { 1319 log.Println("Remove file error:", err) 1320 } 1321 return err 1322 } 1323 1324 rh, err := strconv.Atoi(resp.Header.Get("Content-Length")) 1325 if err != nil || (fs.Size() != int64(rh)) { 1326 if rh != 0 { 1327 1328 if fs.Size() != int64(rh) { 1329 1330 err := errors.New(fileURL + " save failed!") 1331 log.Println(err) 1332 1333 if err := os.Remove(filePath); err != nil { 1334 log.Println("Remove file error:", err) 1335 } 1336 return err 1337 1338 } 1339 return err 1340 } 1341 } 1342 1343 log.Println(fileURL+" download success!", "written: ", written) 1344 return err 1345 } 1346 1347 //PostFile 以POST方式发送内容到 web 端 1348 func PostFile(filepath string, actionurl string, fieldname string) (*http.Response, error) { 1349 bodyBuf := bytes.NewBufferString("") 1350 bodyWriter := multipart.NewWriter(bodyBuf) 1351 1352 // use the body_writer to write the Part headers to the buffer 1353 _, err := bodyWriter.CreateFormFile(fieldname, filepath) 1354 if err != nil { 1355 fmt.Println("error writing to buffer") 1356 return nil, err 1357 } 1358 1359 // the file data will be the second part of the body 1360 fh, err := os.Open(filepath) 1361 if err != nil { 1362 fmt.Println("error opening file") 1363 return nil, err 1364 } 1365 defer fh.Close() 1366 // need to know the boundary to properly close the part myself. 1367 boundary := bodyWriter.Boundary() 1368 closeString := fmt.Sprintf("\r\n--%s--\r\n", boundary) 1369 closeBuf := bytes.NewBufferString(closeString) 1370 // use multi-reader to defer the reading of the file data until writing to the socket buffer. 1371 requestReader := io.MultiReader(bodyBuf, fh, closeBuf) 1372 fi, err := fh.Stat() 1373 if err != nil { 1374 fmt.Printf("Error Stating file: %s", filepath) 1375 return nil, err 1376 } 1377 1378 req, err := http.NewRequest("POST", actionurl, requestReader) 1379 if err != nil { 1380 return nil, err 1381 } 1382 1383 // Set headers for multipart, and Content Length 1384 req.Header.Add("Content-Type", "multipart/form-data; boundary="+boundary) 1385 req.ContentLength = fi.Size() + int64(bodyBuf.Len()) + int64(closeBuf.Len()) 1386 1387 return http.DefaultClient.Do(req) 1388 1389 } 1390 1391 //WriteFile 按指定路径写入内容作为文件 1392 func WriteFile(filepath string, content string) error { 1393 dirpath := fpath.Dir(filepath) 1394 1395 os.MkdirAll(dirpath, 0644) 1396 file, err := os.Create(filepath) 1397 if err != nil { 1398 return err 1399 } 1400 defer file.Close() 1401 1402 writer := bufio.NewWriter(file) 1403 writer.WriteString(content) 1404 return writer.Flush() 1405 } 1406 1407 //MoveFile 移动文件到目标路径 1408 func MoveFile(frompath string, topath string) error { 1409 1410 fromfile, err := os.Open(frompath) 1411 if err != nil { 1412 return err 1413 } 1414 1415 tofile, err := os.OpenFile(topath, os.O_WRONLY|os.O_CREATE, 0644) 1416 if err != nil { 1417 return err 1418 } 1419 io.Copy(tofile, fromfile) 1420 fromfile.Close() 1421 tofile.Close() 1422 os.Remove(frompath) 1423 /* 1424 io.Copy 在一般情况下拷贝不会出错,多个协程访问的时候可能会出现“read ./data/*.png: Access is denied.”的错误, 1425 造成这个错误的原因很可能是由于多个协程争抢打开文件导致,然而实际情况可能报错后却又删除成功。 1426 如果我们根据这个错误作出判断的话就会错上加错,所以在这里不做任何判断,完全由上帝决定好了。 1427 */ 1428 return nil 1429 1430 } 1431 1432 //CheckPassword 检测密码字符是否符合标准 1433 func CheckPassword(password string) (b bool) { 1434 if ok, _ := regexp.MatchString(`^[\@A-Za-z0-9\!\#\$\%\^\&\*\~\{\}\[\]\.\,\<\>\(\)\_\+\=]{4,30}$`, password); !ok { 1435 return false 1436 } 1437 return true 1438 } 1439 1440 //CheckUsername 检测用户名称是否符合标准 1441 func CheckUsername(username string) (b bool) { 1442 if ok, _ := regexp.MatchString("^[\\x{4e00}-\\x{9fa5}A-Z0-9a-z_-]{2,30}$", username); !ok { 1443 return false 1444 } 1445 return true 1446 } 1447 1448 //CheckEmail : 检测伊妹儿是否符合格式 1449 func CheckEmail(email string) (b bool) { 1450 if ok, _ := regexp.MatchString(`^([a-zA-Z0-9._-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$`, email); !ok { 1451 return false 1452 } 1453 return true 1454 } 1455 1456 /* 1457 #gravity可用值有九个,分别是: 1458 1459 西北方 NorthWest:左上角为坐标原点,x轴从左到右,y轴从上到下,也是默认值。 1460 北方 North:上部中间位置为坐标原点,x轴从左到右,y轴从上到下。 1461 东北方 NorthEast:右上角位置为坐标原点,x轴从右到左,y轴从上到下。 1462 西方 West:左边缘中间位置为坐标原点,x轴从左到右,y轴从上到下。 1463 中央 Center:正中间位置为坐标原点,x轴从左到右,y轴从上到下。 1464 东方 East:右边缘的中间位置为坐标原点,x轴从右到左,y轴从上到下。 1465 西南方 SouthWest:左下角为坐标原点,x轴从左到右,y轴从下到上。 1466 南方 South:下边缘的中间为坐标原点,x轴从左到右,y轴从下到上。 1467 东南方 SouthEast:右下角坐标原点,x轴从右到左,y轴从下到上。 1468 1469 */ 1470 /* 1471 func Thumbnail(mode string, input_file string, output_file string, output_size string, output_align string, background string) error { 1472 //预处理gif格式 1473 if strings.HasSuffix(input_file, "gif") { 1474 if Exist(input_file) { 1475 1476 //convert input_file -coalesce m_file 1477 1478 cmd := exec.Command("convert", "-coalesce", input_file, input_file) 1479 err := cmd.Run() 1480 1481 return err 1482 } else { 1483 return errors.New("需要被缩略处理的GIF图片文件并不存在!") 1484 } 1485 } 1486 1487 switch { 1488 case mode == "resize": 1489 if Exist(input_file) { 1490 1491 //convert -resize 256x256^ -gravity center -extent 256x256 src.jpg dest.jpg 1492 //详细使用格式 http://www.imagemagick.org/Usage/resize/ 1493 cmd := exec.Command("convert", "-resize", output_size+"^", "-gravity", output_align, "-extent", output_size, "-background", background, input_file, output_file) 1494 err := cmd.Run() 1495 1496 return err 1497 } else { 1498 return errors.New("需要被缩略处理的图片文件并不存在!") 1499 } 1500 case mode == "crop": 1501 if Exist(input_file) { 1502 //convert -crop 300×400 center src.jpg dest.jpg 从src.jpg坐标为x:10 y:10截取300×400的图片存为dest.jpg 1503 //convert -crop 300×400-10+10 src.jpg dest.jpg 从src.jpg坐标为x:0 y:10截取290×400的图片存为dest.jpg 1504 //详细使用格式 http://www.imagemagick.org/Usage/crop/ 1505 cmd := exec.Command("convert", "-gravity", output_align, "-crop", output_size+"+0+0", "+repage", "-background", background, "-extent", output_size, input_file, output_file) 1506 err := cmd.Run() 1507 1508 return err 1509 } else { 1510 return errors.New("需要被缩略处理的图片文件并不存在!") 1511 } 1512 default: 1513 if Exist(input_file) { 1514 1515 cmd := exec.Command("convert", "-thumbnail", output_size, "-background", background, "-gravity", output_align, "-extent", output_size, input_file, output_file) 1516 err := cmd.Run() 1517 1518 return err 1519 } else { 1520 return errors.New("需要被缩略处理的图片文件并不存在!") 1521 } 1522 } 1523 1524 } 1525 */ 1526 1527 //Thumbnail 对图片进行缩略处理 1528 func Thumbnail(mode string, inputFile string, outputFile string, outputSize string, outputAlign string, background string) error { 1529 r, err := os.Open(inputFile) 1530 if err != nil { 1531 return err 1532 } 1533 defer r.Close() 1534 1535 w, err := os.Create(outputFile) 1536 if err != nil { 1537 return err 1538 } 1539 defer w.Close() 1540 1541 var width, height int 1542 if size := strings.Split(outputSize, "x"); len(size) >= 2 { 1543 width, _ = strconv.Atoi(size[0]) 1544 height, _ = strconv.Atoi(size[1]) 1545 } else { 1546 width, _ = strconv.Atoi(size[0]) 1547 height = width 1548 } 1549 1550 return GraphicsProcess(r, w, width, height, 100) 1551 1552 } 1553 1554 //Watermark 对输入的图片文件作水印处理 1555 func Watermark(watermarkFile string, inputFile string, outputFile string, outputAlign string) error { 1556 r, err := os.Open(inputFile) 1557 if err != nil { 1558 return err 1559 } 1560 defer r.Close() 1561 1562 b, err := ioutil.ReadAll(r) 1563 if err != nil { 1564 return fmt.Errorf("ioutil.ReadAll:%v", err) 1565 } 1566 1567 g, format, err := image.Decode(bytes.NewReader(b)) 1568 if err != nil { 1569 return fmt.Errorf("image.Decode(bytes.NewReader(b)):%v", err) 1570 } 1571 1572 if (format == "png") || (format == "jpeg") { 1573 fr, err := os.Open(watermarkFile) 1574 if err != nil { 1575 return err 1576 } 1577 defer fr.Close() 1578 1579 wk, _, err := image.Decode(fr) 1580 if err != nil { 1581 return (err) 1582 } 1583 1584 dst := image.NewRGBA(g.Bounds()) 1585 gift.New().Draw(dst, g) 1586 //水印放在右下角 1587 padding := 20 1588 x := (g.Bounds().Dx() - (wk.Bounds().Dx() + padding)) 1589 y := (g.Bounds().Dy() - (wk.Bounds().Dy() + padding)) 1590 gift.New().DrawAt(dst, wk, image.Point{x, y}, gift.OverOperator) 1591 1592 w, err := os.Create(outputFile) 1593 if err != nil { 1594 return err 1595 } 1596 1597 if format == "png" { 1598 err := png.Encode(w, dst) 1599 if err != nil { 1600 return err 1601 } 1602 } else { 1603 err := jpeg.Encode(w, dst, &jpeg.Options{Quality: 100}) 1604 if err != nil { 1605 return err 1606 } 1607 } 1608 } else { 1609 return fmt.Errorf("Not support format:%s", format) 1610 } 1611 1612 return nil 1613 } 1614 1615 //Rex 如果文本符合正则表达式则返回真 1616 func Rex(text string, iregexp string) (b bool) { 1617 if ok, _ := regexp.MatchString(iregexp, text); !ok { 1618 return false 1619 } 1620 return true 1621 } 1622 1623 //Exist : if file exist return true 1624 func Exist(filename string) bool { 1625 _, err := os.Stat(filename) 1626 return err == nil || os.IsExist(err) 1627 } 1628 1629 //RsaAesSendPacket 发送报文 是否加密 HTTP状态 动作URL 数据内容 RSA公匙 1630 func RsaAesSendPacket(encrypt bool, status string, actionurl string, content string, aesKey string, aesPublicKey string, rsaPublicKey []byte) (*http.Response, error) { 1631 /* 1632 1.对数据进行AES加密 1633 2.对AES密匙KEY进行RSA加密 1634 3.POST的时候,把RSA密码串放置于URL发送 1635 4.POST的时候,把AES密码串放置于BODY发送 1636 */ 1637 //只有公钥则只能加密 公钥私钥都有才能解密 所以私匙不能在客户端公开 客户端获取的内容由服务端的权限控制决定 1638 var bodyBuf io.Reader 1639 if encrypt { 1640 // AES对内容进行加密 1641 aesEncryptContent, err := AesCFBEncrypt(content, aesKey, aesPublicKey) 1642 if err != nil { 1643 return nil, err 1644 } 1645 bodyBuf = bytes.NewBufferString(aesEncryptContent) 1646 1647 // 对AES密匙aesKey进行RSA加密 1648 rsaEncryptContent, err := RsaEncrypt([]byte(aesKey), rsaPublicKey) 1649 if err != nil { 1650 return nil, err 1651 } 1652 //转换RSA密文BYTE编码为16进制字符串 1653 aesKey = fmt.Sprintf("%x", rsaEncryptContent) 1654 } else { 1655 //无需加密 1656 bodyBuf = bytes.NewBufferString(content) 1657 1658 } 1659 1660 //hash就是各种内容的混合体加key的hash值,验证这个hash是否一致来保证内容不被非法更改 1661 createtime := strconv.Itoa(int(time.Now().UnixNano())) 1662 //hash+createtime+aeskey 1663 actionurl = actionurl + "?hash=" + EncryptHash(status+createtime+string(content)+string(rsaPublicKey), nil) + "-" + createtime + "-" + aesKey 1664 1665 req, err := http.NewRequest(status, actionurl, bodyBuf) 1666 if err != nil { 1667 return nil, err 1668 } 1669 hd, err := http.DefaultClient.Do(req) 1670 return hd, err 1671 } 1672 1673 //RsaAesReceivingPacket 接受加密数据包 1674 func RsaAesReceivingPacket(decrypt bool, hash string, status string, content []byte, aesPublicKey string, rsaPublicKey []byte, rsaPrivateKey []byte) ([]byte, error) { 1675 1676 //防擅改校验数据 1677 if hash != "" { 1678 /* 1679 1.对AES数据进行AES解密得出内容 1680 2.对RSA数据进行RSA解密得出AES密匙KEY 1681 */ 1682 1683 //分解hash+createtime+aeskey 1684 s := strings.Split(hash, "-") 1685 hash = s[0] 1686 createtime := s[1] 1687 aseKey := s[2] 1688 1689 //若 decrypt为真则进行解密 1690 if decrypt { 1691 if aseKey != "" { 1692 1693 //对16进制字符串aseKey进行解码 1694 if x, err := hex.DecodeString(aseKey); err == nil { 1695 1696 //RSA解密 得出 AES KEY 1697 rsaDecryptContent, err := RsaDecrypt(x, rsaPrivateKey) 1698 if err != nil { 1699 return nil, err 1700 } 1701 1702 //还原 aseKey 1703 aseKey = string(rsaDecryptContent) 1704 1705 //对AES数据进行AES解密得出内容 1706 aesDecryptContent, err := AesCFBDecrypt(string(content), aseKey, aesPublicKey) 1707 if err != nil { 1708 return nil, err 1709 } 1710 1711 content = []byte(aesDecryptContent) 1712 1713 } else { 1714 //16进制解码错误 1715 return nil, err 1716 } 1717 1718 } else { 1719 return nil, errors.New("AES KEY为空无法进行解密") 1720 } 1721 } 1722 1723 if (hash != "") && (createtime != "") { 1724 1725 if ValidateHash(hash, status+createtime+string(content)+string(rsaPublicKey)) { 1726 //返回数据明文 1727 return content, nil 1728 } 1729 1730 return nil, errors.New("报文无法通过数据校验") 1731 1732 } 1733 } 1734 1735 return nil, errors.New("数据校验HASH值为空") 1736 1737 } 1738 1739 //SendPacket 发送报文 1740 func SendPacket(status string, actionurl string, content string, ckJar *cookiejar.Jar) (*http.Response, error) { 1741 1742 client := &http.Client{ 1743 Jar: ckJar, 1744 } 1745 var bodyBuf io.Reader 1746 bodyBuf = bytes.NewBufferString(content) 1747 req, err := http.NewRequest(status, actionurl, bodyBuf) 1748 if err != nil { 1749 return nil, err 1750 } 1751 hd, err := client.Do(req) 1752 return hd, err 1753 } 1754 1755 //GetBanner : Get one of images 1756 func GetBanner(content string) (string, error) { 1757 1758 if imgs, num := GetImages(content); num > 0 { 1759 1760 for _, v := range imgs { 1761 // 只获取本地图片,外部图片不太可靠 1762 if IsLocal(v) { 1763 if Exist(URL2local(v)) { 1764 1765 return v, nil 1766 } 1767 } 1768 return v, errors.New("GetBanner没有图片链接") 1769 } 1770 } 1771 return "", errors.New("GetBanner没有图片链接") 1772 } 1773 1774 //IsLocal : if is local path then return true 1775 func IsLocal(path string) bool { 1776 if path != "" { 1777 /* 1778 把本地路径的无点形式转为有点形式 1779 转换之后,如果之前传入的是恰好是一个网址而不是本地路径,则在后面的分拣中会把它列入并非本地路径的行列 1780 因为本地路径在本系统中是预设想必为当前网站项目文件夹范围内的 ./root/path 而不能跳出 到 ../root/path外, 1781 所以跳出到 ../root/path 外的路径必定不是本地路径! 1782 */ 1783 path = URL2local(path) 1784 1785 //检查带点的本地路径 1786 s := strings.SplitN(path, ".", -1) 1787 if len(s) > 1 && len(s) < 4 { 1788 //通过路径的前缀是否为"/"判断是不是本地文件 1789 if s[1] != "" { 1790 if strings.HasPrefix(s[1], "/") { 1791 return true 1792 } 1793 // 第一轮次检查的时候碰上"/"开头的本地路径会判断不出来,需要再进行第2次判断"/"开头的情况 1794 ss := strings.SplitN("."+s[1], ".", -1) 1795 if len(ss) > 1 && len(ss) < 4 { 1796 //通过路径的前缀是否为"/"判断是不是本地文件 1797 if ss[1] != "" { 1798 if strings.HasPrefix(ss[1], "/") { 1799 return true 1800 } 1801 return false 1802 } 1803 return false 1804 } 1805 return false 1806 1807 } 1808 return false 1809 } 1810 } 1811 return false 1812 } 1813 1814 //Local2url is turn the local path to url 1815 func Local2url(path string) string { 1816 /* 1817 if strings.HasPrefix(path, "./") { 1818 path = strings.Replace(path, "./", "/", -1) 1819 } 1820 */ 1821 if (!strings.HasPrefix(path, "http") || !strings.HasPrefix(path, "ftp")) && (!strings.HasPrefix(path, "/")) { 1822 path = "/" + path 1823 } else if strings.HasPrefix(path, "./") { 1824 path = strings.Replace(path, "./", "/", 1) 1825 } 1826 return path 1827 } 1828 1829 //URL2local is turn the url to local path 1830 func URL2local(path string) string { 1831 1832 if (!strings.HasPrefix(path, "http") || !strings.HasPrefix(path, "ftp")) && (!strings.HasPrefix(path, "/")) { 1833 path = "./" + path 1834 } else if strings.HasPrefix(path, "/") { 1835 path = strings.Replace(path, "/", "./", 1) 1836 } 1837 return path 1838 } 1839 1840 //SetSuffix 设置后缀 1841 func SetSuffix(content string, str string) string { 1842 1843 content = URL2local(content) 1844 if len(content) > 0 { 1845 s := strings.SplitN(content, ".", -1) 1846 if len(s) > 1 && len(s) < 4 { 1847 // 判断是不是本地文件或本地路径 1848 if s[1] != "" && IsLocal(s[1]) { 1849 return Local2url(s[1] + str) 1850 } 1851 return Local2url(content) 1852 } 1853 } 1854 return "" 1855 } 1856 1857 func staticProcess(g image.Image, format string, w io.Writer, width, height, quality int) error { 1858 1859 filter := gift.New( 1860 // high-quality resampling with pixel mixing 1861 //gift.ResizeToFit(width, height, gift.LinearResampling), 1862 //gift.ResizeToFill(width, height, gift.LanczosResampling, gift.CenterAnchor), 1863 //gift.ResizeToFill(width, height, gift.LinearResampling, gift.CenterAnchor), 1864 1865 //gift.ResizeToFit(width, height, gift.LanczosResampling), 1866 1867 //gift.Resize(width, height, gift.LinearResampling), 1868 gift.Resize(width, height, gift.LanczosResampling), 1869 //gift.CropToSize(width, height, gift.CenterAnchor), 1870 ) 1871 1872 //tmp := image.NewNRGBA(g.Bounds()) 1873 //gift.New().DrawAt(tmp, g, g.Bounds().Min, gift.OverOperator) 1874 //gift.New().Draw(tmp, g) 1875 //dst := image.NewRGBA(filter.Bounds(tmp.Bounds())) 1876 //filter.Draw(dst, tmp) 1877 1878 dst := image.NewRGBA(filter.Bounds(g.Bounds())) 1879 filter.Draw(dst, g) 1880 1881 var err error 1882 if format == "png" { 1883 err = png.Encode(w, dst) 1884 if err != nil { 1885 return err 1886 } 1887 } else { 1888 err = jpeg.Encode(w, dst, &jpeg.Options{Quality: quality}) 1889 if err != nil { 1890 return err 1891 } 1892 } 1893 1894 return nil 1895 } 1896 1897 func animateProcess(g *gif.GIF, w io.Writer, width, height int) error { 1898 1899 filter := gift.New( 1900 // high-quality resampling with pixel mixing 1901 //gift.ResizeToFit(width, height, gift.LanczosResampling), 1902 //gift.ResizeToFill(width, height, gift.LanczosResampling, gift.CenterAnchor), 1903 //gift.Resize(width, height, gift.LinearResampling), 1904 gift.Resize(width, height, gift.LanczosResampling), 1905 //gift.CropToSize(width, height, gift.CenterAnchor), 1906 ) 1907 1908 var ng = make([]*image.Paletted, 0) 1909 tmp := image.NewNRGBA(g.Image[0].Bounds()) 1910 for _, i := range g.Image { 1911 gift.New().DrawAt(tmp, i, i.Bounds().Min, gift.OverOperator) 1912 dst := image.NewPaletted(filter.Bounds(tmp.Bounds()), i.Palette) 1913 filter.Draw(dst, tmp) 1914 ng = append(ng, dst) 1915 } 1916 1917 d := &gif.GIF{ 1918 Image: ng, 1919 Delay: g.Delay, 1920 LoopCount: g.LoopCount, 1921 } 1922 1923 err := gif.EncodeAll(w, d) 1924 if err != nil { 1925 return err 1926 } 1927 1928 return nil 1929 } 1930 1931 //GraphicsProcess 图像处理过程 1932 func GraphicsProcess(r io.Reader, w io.Writer, width, height, quality int) error { 1933 1934 b, err := ioutil.ReadAll(r) 1935 if err != nil { 1936 return fmt.Errorf("ioutil.ReadAll:%v", err) 1937 } 1938 1939 g, format, err := image.Decode(bytes.NewReader(b)) 1940 if err != nil { 1941 return fmt.Errorf("image.Decode(bytes.NewReader(b)):%v", err) 1942 } 1943 1944 if (format == "png") || (format == "jpeg") { 1945 return staticProcess(g, format, w, width, height, quality) 1946 } else if format == "gif" { 1947 g, err := gif.DecodeAll(bytes.NewReader(b)) 1948 if err != nil { 1949 return fmt.Errorf("gif.DecodeAll(bytes.NewReader(b)):%v", err) 1950 } 1951 return animateProcess(g, w, width, height) 1952 } else { 1953 return errors.New("bad format") 1954 } 1955 } 1956 1957 //GetBannerThumbnail 从内容获取banner 1958 func GetBannerThumbnail(content string) (string, error) { 1959 //开始提取img 1960 s, e := GetBanner(content) 1961 if e == nil { 1962 //配置缩略图 1963 inputFile := URL2local(s) 1964 outputFile := URL2local(SetSuffix(s, "_banner.jpg")) 1965 outputSize := "680x300" 1966 outputAlign := "center" 1967 background := "black" 1968 1969 //处理缩略图 1970 err := Thumbnail("crop", inputFile, outputFile, outputSize, outputAlign, background) 1971 if err == nil { 1972 return Local2url(outputFile), err 1973 } 1974 log.Println("GetBannerThumbnail生成缩略图出错:", err) 1975 1976 if e := os.Remove(outputFile); e != nil { 1977 fmt.Println("GetBannerThumbnail清除残余缩略图文件出错:", e) 1978 return "", e 1979 } 1980 return "", err 1981 } 1982 1983 return "", e 1984 } 1985 1986 //GetThumbnails get thumbnails from the content 1987 func GetThumbnails(content string) (thumbnails string, thumbnailslarge string, thumbnailsmedium string, thumbnailssmall string, err error) { 1988 /* 1989 Thumbnails string //Original remote file , 请在外部处理成200x200再输入进来 1990 ThumbnailsLarge string //100x100 1991 ThumbnailsMedium string //48x48 1992 ThumbnailsSmall string //32x32 1993 */ 1994 //开始提取img 默认所有图片为本地文件 1995 originalFile, e := GetBanner(content) 1996 if e == nil { 1997 1998 //配置缩略图 1999 inputFile := URL2local(originalFile) 2000 outputFileLarge := URL2local(SetSuffix(originalFile, "_large.jpg")) 2001 outputFileMedium := URL2local(SetSuffix(originalFile, "_medium.jpg")) 2002 outputFileSmall := URL2local(SetSuffix(originalFile, "_small.jpg")) 2003 outputSizeLarge := "100x100" 2004 outputSizeMedium := "48x48" 2005 outputSizeSmall := "32x32" 2006 outputAlign := "center" 2007 background := "#ffffff" 2008 2009 //处理缩略图 2010 //原始文件 2011 thumbnails = originalFile 2012 2013 //大缩略图 2014 if err := Thumbnail("resize", inputFile, outputFileLarge, outputSizeLarge, outputAlign, background); err == nil { 2015 2016 thumbnailslarge = Local2url(outputFileLarge) 2017 } else { 2018 2019 fmt.Println("GetThumbnails生成thumbnailslarge缩略图出错:", err) 2020 2021 if e := os.Remove(outputFileLarge); e != nil { 2022 fmt.Println("GetThumbnails清除残余thumbnailslarge缩略图文件出错:", e) 2023 2024 } 2025 } 2026 2027 //中缩略图 2028 if err := Thumbnail("resize", inputFile, outputFileMedium, outputSizeMedium, outputAlign, background); err == nil { 2029 2030 thumbnailsmedium = Local2url(outputFileMedium) 2031 } else { 2032 2033 fmt.Println("GetThumbnails生成output_file_Medium缩略图出错:", err) 2034 2035 if e := os.Remove(outputFileMedium); e != nil { 2036 fmt.Println("GetThumbnails清除残余output_file_Medium缩略图文件出错:", e) 2037 2038 } 2039 } 2040 2041 //小缩略图 2042 if err := Thumbnail("resize", inputFile, outputFileSmall, outputSizeSmall, outputAlign, background); err == nil { 2043 2044 thumbnailssmall = Local2url(outputFileSmall) 2045 } else { 2046 2047 log.Println("GetThumbnails生成outputFileSmall缩略图出错:", err) 2048 if e := os.Remove(outputFileSmall); e != nil { 2049 fmt.Println("GetThumbnails清除残余outputFileSmall缩略图文件出错:", e) 2050 2051 } 2052 } 2053 return thumbnails, thumbnailslarge, thumbnailsmedium, thumbnailssmall, nil 2054 } 2055 return "", "", "", "", e 2056 } 2057 2058 //MakeThumbnails 对指定图片文件进行缩略处理 2059 func MakeThumbnails(localpath string) (thumbnails string, thumbnailslarge string, thumbnailsmedium string, thumbnailssmall string, err error) { 2060 /* 2061 Thumbnails string //Original remote file 2062 ThumbnailsLarge string //200x300 2063 ThumbnailsMedium string //200x150 2064 ThumbnailsSmall string //70x70 2065 */ 2066 //开始提取img 默认所有图片为本地文件 2067 if originalFile := URL2local(localpath); originalFile != "" { 2068 2069 //配置缩略图 2070 inputFile := URL2local(originalFile) 2071 outputFileLarge := URL2local(SetSuffix(originalFile, "_large.jpg")) 2072 outputFileMedium := URL2local(SetSuffix(originalFile, "_medium.jpg")) 2073 outputFileSmall := URL2local(SetSuffix(originalFile, "_small.jpg")) 2074 outputSizeLarge := "200x300" 2075 outputSizeMedium := "200x150" 2076 outputSizeSmall := "70x70" 2077 outputAlign := "center" 2078 background := "#ffffff" 2079 2080 //处理缩略图 2081 //原始文件 也缩略处理以适合内容框大小 2082 if err := Thumbnail("thumbnail", inputFile, originalFile, "696x", outputAlign, background); err == nil { 2083 watermarkFile := "./static/yougam/img/watermark.png" 2084 Watermark(watermarkFile, originalFile, originalFile, "SouthEast") 2085 thumbnails = Local2url(originalFile) 2086 } else { 2087 2088 fmt.Println("MakeThumbnails生成thumbnails缩略图出错:", err) 2089 2090 if e := os.Remove(originalFile); e != nil { 2091 fmt.Println("MakeThumbnails清除残余thumbnails缩略图文件出错:", e) 2092 2093 } 2094 } 2095 2096 //大缩略图 2097 if err := Thumbnail("resize", inputFile, outputFileLarge, outputSizeLarge, outputAlign, background); err == nil { 2098 2099 thumbnailslarge = Local2url(outputFileLarge) 2100 } else { 2101 log.Println("MakeThumbnails生成thumbnailslarge缩略图出错:", err) 2102 if e := os.Remove(outputFileLarge); e != nil { 2103 fmt.Println("MakeThumbnails清除残余thumbnailslarge缩略图文件出错:", e) 2104 2105 } 2106 } 2107 2108 //中缩略图 2109 if err := Thumbnail("resize", inputFile, outputFileMedium, outputSizeMedium, outputAlign, background); err == nil { 2110 2111 thumbnailsmedium = Local2url(outputFileMedium) 2112 } else { 2113 2114 fmt.Println("MakeThumbnails生成outputFileMedium缩略图出错:", err) 2115 2116 if e := os.Remove(outputFileMedium); e != nil { 2117 fmt.Println("MakeThumbnails清除残余outputFileMedium缩略图文件出错:", e) 2118 2119 } 2120 } 2121 2122 //小缩略图 2123 if err := Thumbnail("resize", inputFile, outputFileSmall, outputSizeSmall, outputAlign, background); err == nil { 2124 2125 thumbnailssmall = Local2url(outputFileSmall) 2126 } else { 2127 log.Println("MakeThumbnails生成outputFileSmall缩略图出错:", err) 2128 if e := os.Remove(outputFileSmall); e != nil { 2129 log.Println("MakeThumbnails清除残余outputFileSmall缩略图文件出错:", e) 2130 } 2131 } 2132 return thumbnails, thumbnailslarge, thumbnailsmedium, thumbnailssmall, nil 2133 } 2134 return "", "", "", "", errors.New("Error: 输入的图片路径为空!") 2135 } 2136 2137 //GetImages 返回 图片url列表集合 2138 func GetImages(content string) (imgs []string, num int) { 2139 2140 //替换HTML的空白字符为空格 2141 ren := regexp.MustCompile(`\s`) //ns*r 2142 bodystr := ren.ReplaceAllString(content, " ") 2143 2144 //匹配所有图片 2145 //rem := regexp.MustCompile(`<img.*?src="(.+?)".*?`) //匹配最前面的图 2146 rem := regexp.MustCompile(`<img.+?src="(.+?)".*?`) //匹配最前面的图 2147 imgUrls := rem.FindAllSubmatch([]byte(bodystr), -1) 2148 2149 for _, bv := range imgUrls { 2150 if m := string(bv[1]); m != "" { 2151 2152 if !IsContainsSets(imgs, m) { 2153 imgs = append(imgs, m) 2154 } 2155 } 2156 } 2157 2158 return imgs, len(imgs) 2159 } 2160 2161 //Base64Encoding 对内容进行标准base64编码 2162 func Base64Encoding(s string) string { 2163 var buf bytes.Buffer 2164 encoder := base64.NewEncoder(base64.StdEncoding, &buf) 2165 defer encoder.Close() 2166 encoder.Write([]byte(s)) 2167 return buf.String() 2168 } 2169 2170 //GetPage 返回获得的网页内容 2171 func GetPage(url string) (string, error) { 2172 2173 //ua := "Mozilla/5.0 (Windows; U; Windows NT 8.8; en-US) AppleWebKit/883.13 (KHTML, like Gecko) Chrome/88.3.13.87 Safari/883.13" 2174 ua := "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.92 Safari/537.1 yougam.Com" 2175 client := &http.Client{} 2176 2177 req, err := http.NewRequest("GET", url, nil) 2178 if err != nil { 2179 return "", err 2180 } 2181 2182 req.Header.Set("User-Agent", ua) 2183 2184 resp, err := client.Do(req) 2185 if err != nil { 2186 return "", err 2187 } 2188 2189 defer resp.Body.Close() 2190 body, err := ioutil.ReadAll(resp.Body) 2191 return string(body), err 2192 2193 } 2194 2195 //DifferenceSets 差集 2196 func DifferenceSets(a []string, b []string) []string { 2197 2198 var f = make([]string, 0) 2199 2200 for _, v := range a { 2201 //如果a集合某元素存在于b集合中 2202 var in bool 2203 for _, vv := range b { 2204 if v == vv { 2205 in = true 2206 break 2207 } 2208 } 2209 if !in { 2210 f = append(f, v) 2211 } 2212 } 2213 return f 2214 } 2215 2216 //IntersectionSets 交集 2217 func IntersectionSets(fora []string, forb []string) []string { 2218 2219 i, c, d := []string{}, []string{}, []string{} 2220 if len(fora) > len(forb) { 2221 2222 c = forb 2223 d = fora 2224 2225 } else { 2226 2227 c = fora 2228 d = forb 2229 } 2230 for _, v := range c { 2231 2232 //如果c集合中某元素v存在于d集合中 2233 for _, vv := range d { 2234 if v == vv { 2235 i = append(i, v) 2236 break 2237 } 2238 } 2239 } 2240 return i 2241 } 2242 2243 //SymmetricDifferenceSets 对称差=并集-交集 即是 并集和交集的差集就是对称差 2244 func SymmetricDifferenceSets(fora []string, forb []string) []string { 2245 2246 return DifferenceSets(UnionSets(fora, forb), IntersectionSets(fora, forb)) 2247 } 2248 2249 //UnionSets 并集 2250 func UnionSets(fora []string, forb []string) []string { 2251 uvalue := []string{} 2252 //求两个字符串数组的并集 2253 for _, v := range fora { 2254 if IsContainsSets(uvalue, v) { 2255 continue 2256 } else { 2257 uvalue = append(uvalue, v) 2258 } 2259 2260 } 2261 for _, v := range forb { 2262 if IsContainsSets(uvalue, v) { 2263 continue 2264 } else { 2265 uvalue = append(uvalue, v) 2266 } 2267 } 2268 2269 return uvalue 2270 } 2271 2272 //IsContainsSets if is contains then return true 2273 func IsContainsSets(values []string, ivalue string) bool { 2274 for _, v := range values { 2275 2276 if v == ivalue { 2277 return true 2278 } 2279 } 2280 return false 2281 } 2282 2283 //DelLostImages delete the lost images 2284 func DelLostImages(oldz string, newz string) { 2285 2286 oldfiles, onum := GetImages(oldz) 2287 newfiles, nnum := GetImages(newz) 2288 2289 //初步过滤门槛,提高效率,因为下面的操作太多循环,能避免进入则避免 2290 if (onum > 0 && nnum > 0) || (onum > 0 && nnum < 1) || (onum == nnum) { 2291 2292 oldfilesLocal := []string{} 2293 newfilesLocal := []string{} 2294 2295 for _, v := range oldfiles { 2296 if IsLocal(v) { 2297 oldfilesLocal = append(oldfilesLocal, v) 2298 //如果本地同时也存在banner缓存文件,则加入旧图集合中,等待后面一次性删除 2299 if p := URL2local(SetSuffix(v, "_banner.jpg")); Exist(p) { 2300 oldfilesLocal = append(oldfilesLocal, p) 2301 } 2302 } 2303 } 2304 //fmt.Println("旧图集合:", oldfilesLocal) 2305 2306 for _, v := range newfiles { 2307 if IsLocal(v) { 2308 newfilesLocal = append(newfilesLocal, v) 2309 } 2310 } 2311 //fmt.Println("新图集合:", newfiles_local) 2312 2313 //旧图集合-新图集合 =待删图集 2314 for k, v := range DifferenceSets(oldfilesLocal, newfilesLocal) { 2315 if p := URL2local(v); Exist(p) { //如若文件存在,则处理,否则忽略 2316 log.Println("删除文件:", p) 2317 if err := os.Remove(p); err != nil { 2318 log.Println("#", k, ",DEL FILE ERROR:", err) 2319 } 2320 } 2321 } 2322 } 2323 2324 } 2325 2326 //StringToUTF16 字符串转换来unit16 2327 func StringToUTF16(s string) []uint16 { 2328 return utf16.Encode([]rune(s + "\x00")) 2329 } 2330 2331 //VerifyUserfile verify the user file 2332 func VerifyUserfile(path string, usr string) bool { 2333 fname := fpath.Base(path)[0:48] 2334 if fhashed, e := Filehash(URL2local(path), nil); e == nil { 2335 return ValidateHash(fname, fhashed+usr) 2336 } 2337 return false 2338 } 2339 2340 //AtUsers 获取文本中 @user 中的用户名集合 2341 func AtUsers(content string) (usrs []string) { 2342 // 新浪微博中的用户名格式为是“4-30个字符,支持中英文、数字、"_"或减号” 2343 //也就是说,支持中文、字母、数字、下划线及减号,并且是4到30个字符,这里 汉字作为一个字符 2344 2345 rx := regexp.MustCompile("@([\\x{4e00}-\\x{9fa5}A-Z0-9a-z_-]+)") 2346 //^[\\x{4e00}-\\x{9fa5}]+$ 2347 //rx := regexp.MustCompile("@[^,,::\\s@]{4,30}") 2348 atusr := rx.FindAllSubmatch([]byte(content), -1) 2349 for _, v := range atusr { 2350 if m := string(v[1]); m != "" { 2351 //usrs = append(usrs, m) 2352 if IsContainsSets(usrs, m) { 2353 continue 2354 } else { 2355 usrs = append(usrs, m) 2356 } 2357 } 2358 } 2359 2360 return usrs 2361 } 2362 2363 //AtPages 获取文本中 @urls 的网址集合 ###AtPages函数的作用是提取@后面的url网址,并不是提取图片,请注意! 2364 func AtPages(content string) ([]string, string) { 2365 urls := []string{} 2366 rxs := "@([a-zA-z]+://[^\\s]*)" 2367 rx := regexp.MustCompile(rxs) 2368 2369 aturl := rx.FindAllSubmatch([]byte(content), -1) 2370 2371 if len(aturl) > 0 { 2372 2373 for _, v := range aturl { 2374 if m := string(v[0]); m != "" { 2375 2376 if !IsContainsSets(urls, m[1:]) { 2377 //替换@link链接 2378 content = strings.Replace(content, m, 2379 "<a href='/url/?localtion="+m[1:]+"' target='_blank' rel='nofollow'><span>@</span><span>"+m[1:]+"</span></a>", -1) 2380 2381 urls = append(urls, m[1:]) 2382 } 2383 } 2384 } 2385 } 2386 2387 return urls, content 2388 } 2389 2390 //AtWhois get user by at 2391 func AtWhois(content string) []string { 2392 whoisz := []string{} 2393 //@[^,,::\s@]+ 2394 //@[\u4e00-\u9fa5a-zA-Z0-9_-]{4,30} 2395 rxs := "@([\u4e00-\u9fa5a-zA-Z0-9_-]+[^,,::\\s@]*)" 2396 rx := regexp.MustCompile(rxs) 2397 2398 atWhois := rx.FindAllSubmatch([]byte(content), -1) 2399 2400 if len(atWhois) > 0 { 2401 2402 for _, v := range atWhois { 2403 if m := string(v[0]); m != "" { 2404 2405 if !IsContainsSets(whoisz, m[1:]) { 2406 2407 whoisz = append(whoisz, m[1:]) 2408 } 2409 } 2410 } 2411 } 2412 2413 return whoisz 2414 } 2415 2416 //AtPagesGetImages 从网址的内容中提取图片 2417 func AtPagesGetImages(content string) ([]string, string) { 2418 imgs := []string{} 2419 links, content := AtPages(content) 2420 for _, v := range links { 2421 2422 page, _ := GetPage(v) 2423 imgz, n := GetImages(page) 2424 2425 if n > 0 { 2426 for _, vv := range imgz { 2427 //vv为单图url 相对路径的处理较为复杂,这里暂时抛弃相对路径的图片 后续再修正 2428 if strings.HasPrefix(vv, "/") { 2429 2430 if strings.HasSuffix(v, "/") { 2431 vv = v + vv[1:] 2432 } else { 2433 vv = v + vv 2434 } 2435 2436 //vv = v + vv[1:] 2437 } else { 2438 vv = FixURL(v, vv) 2439 } 2440 if !strings.HasPrefix(vv, "../") { 2441 2442 if !strings.HasSuffix(v, "js") { 2443 if !IsContainsSets(imgs, vv) { 2444 imgs = append(imgs, vv) 2445 } 2446 } 2447 } 2448 2449 } 2450 } 2451 } 2452 return imgs, content 2453 } 2454 2455 //FixURL 修正url 2456 func FixURL(currentURL, url string) string { 2457 2458 re1, _ := regexp.Compile("http[s]?://[^/]+") 2459 destrooturl := re1.FindString(currentURL) 2460 2461 //当url为://wah/x/t.png 2462 if strings.HasPrefix(url, "//") { 2463 url = "http:" + url 2464 } else if strings.HasPrefix(url, "/") { 2465 // re1,_ := regexp.Compile("http[s]?://[^/]+") 2466 // destrooturl := re1.FindString(currentURL) 2467 url = destrooturl + url 2468 } 2469 2470 //当url为:"../wahaha/xiaoxixi/tupian.png"、"./wahaha/xiaoxixi/tupian.png"、"wahaha/xiaoxixi/tupian.png" 2471 if !strings.HasPrefix(url, "/") && !strings.HasPrefix(url, "http") && !strings.HasPrefix(url, "https") { 2472 // current_url = strings.TrimSuffix(current_url, "/") 2473 if destrooturl == currentURL { 2474 url = currentURL + "/" + url 2475 } else { 2476 re2, _ := regexp.Compile("[^/]+?$") 2477 url = re2.ReplaceAllString(currentURL, "") + url 2478 } 2479 2480 } 2481 2482 return url 2483 } 2484 2485 //PingFile 检测远端文件i是否可用 2486 func PingFile(url string) bool { 2487 r, e := http.Head(url) 2488 if e != nil { 2489 return false 2490 } 2491 if (r.Status == "404") || (r.Status != "200") { 2492 return false 2493 } 2494 return true 2495 } 2496 2497 //Split 分割tags imgs等自行用特定符号组合的字符串为列表 2498 func Split(content string, str string) []string { 2499 2500 if content != "" && str != "" { 2501 2502 ss := []string{} 2503 s := strings.SplitN(content, str, -1) 2504 for _, v := range s { 2505 if v != "" { 2506 ss = append(ss, v) 2507 } 2508 } 2509 2510 return ss 2511 2512 } 2513 return nil 2514 } 2515 2516 //Metric 返回数字带数量级单位 千对k 百万对M 京对G 2517 func Metric(n int64) string { 2518 2519 switch { 2520 case n >= 1000: 2521 return fmt.Sprint(n/1000, "k") 2522 case n >= 1000000: 2523 return fmt.Sprint(n/1000000, "m") 2524 default: 2525 return fmt.Sprint(n) 2526 } 2527 } 2528 2529 //Gravatar 根据用户邮箱显示Gravatar头像 2530 func Gravatar(email string, height int) string { 2531 if email != "" && height != 0 { 2532 // 将邮箱转换成MD5哈希值,并设置图像的大小为height像素 2533 usergravatar := `http://www.gravatar.com/avatar/` + MD5(email) + `?s=` + strconv.Itoa(height) 2534 return usergravatar 2535 } 2536 return "" 2537 } 2538 2539 //Markdown 转换markdown为html模板对象 2540 func Markdown(md string) template.HTML { 2541 return template.HTML(Markdown2String(md)) 2542 } 2543 2544 //Markdown2String md内容转为string 2545 func Markdown2String(md string) string { 2546 return string(Markdown2Byte(md)) 2547 } 2548 2549 //Markdown2Byte md内容转为bytes 2550 func Markdown2Byte(md string) []byte { 2551 unsafe := blackfriday.MarkdownCommon([]byte(md)) 2552 return bluemonday.UGCPolicy().SanitizeBytes(unsafe) 2553 } 2554 2555 //Markdown2Text md内容转为text 2556 func Markdown2Text(md string) string { 2557 re, _ := regexp.Compile("\\<[\\S\\s]+?\\>") 2558 /* 2559 text := re.ReplaceAllString(string(blackfriday.MarkdownCommon([]byte(md))), "") 2560 text = strings.Replace(text, "&#34;", """, -1) // " """ 2561 text = strings.Replace(text, "&#39;", "'", -1) //' ''' 2562 text = strings.Replace(text, "&lt;", "<", -1) // < 2563 text = strings.Replace(text, "&gt;", ">", -1) // > 2564 text = strings.Replace(text, "…", "…", -1) // 省略号 2565 */ 2566 2567 text := re.ReplaceAllString(Markdown2String(md), "") 2568 return text 2569 } 2570 2571 //String2Time 时间字符串转换为时间类型 2572 func String2Time(strtime string) (time.Time, error) { 2573 return time.ParseInLocation("2006-01-02 15:04:05.999999999", strtime, time.Local) 2574 } 2575 2576 //String2UnixNano 时间字符串转换为纳秒级的时间戳 2577 func String2UnixNano(strtime string) (int64, error) { 2578 t, e := time.ParseInLocation("2006-01-02 15:04:05.999999999", strtime, time.Local) 2579 if e != nil { 2580 return -1, e 2581 } 2582 return t.UnixNano(), nil 2583 } 2584 2585 //GetVideoAddress 调用you-get命令对输入的视频网站网址进行抽取真实视频源网址 2586 func GetVideoAddress(input string) string { 2587 2588 cmd := exec.Command("you-get", "-F", "mp4", "-u", input) 2589 b, _ := cmd.Output() 2590 cmd.Run() 2591 2592 rx := regexp.MustCompile(`\['(.+?)'\]`) 2593 urls := rx.Find(b) 2594 output := strings.Replace(string(urls), "[", "", -1) 2595 output = strings.Replace(output, "]", "", -1) 2596 output = strings.Replace(output, "'", "", -1) 2597 2598 return output 2599 2600 } 2601 2602 //Tag4Video 获取视频标签对内的视频网址 2603 func Tag4Video(input string) string { 2604 2605 rx := regexp.MustCompile(`\[v\](.*)\[/v\]`) 2606 tmpurl := rx.Find([]byte(input)) 2607 url := strings.Replace(string(tmpurl), "[v]", "", -1) 2608 url = strings.Replace(url, "[/v]", "", -1) 2609 2610 return url 2611 } 2612 2613 //VideoTags 调用you-get命令对视频标签进行解析 2614 func VideoTags(in string) string { 2615 rx := regexp.MustCompile(`\[video\](.+?)\[/video\]`) 2616 text := string(rx.Find([]byte(in))) 2617 2618 if text != "" { 2619 text = strings.Replace(text, "[video]", "", -1) 2620 text = strings.Replace(text, "[/video]", "", -1) 2621 text = GetVideoAddress(text) 2622 2623 if text != "" { 2624 s := Split(text, ",") 2625 2626 if len(s) >= 1 { 2627 text = "" 2628 for k, v := range s { 2629 text = text + ` 2630 { 2631 src : [ 2632 '` + v + `' 2633 ], 2634 poster : '', 2635 title : 'Video ` + strconv.Itoa(k) + `' 2636 }` 2637 if k != 0 || k != (len(s)-1) { 2638 text = text + `,` 2639 } 2640 } 2641 } 2642 2643 } 2644 } 2645 2646 return text 2647 } 2648 2649 //Htmlquote HTML编码为实体符号 2650 func Htmlquote(text string) string { 2651 /* 2652 Encodes `text` for raw use in HTML. 2653 >>> htmlquote("<'&\\">") 2654 '<'&">' 2655 */ 2656 2657 text = strings.Replace(text, "&", "&", -1) // Must be done first! 2658 text = strings.Replace(text, "…", "…", -1) 2659 text = strings.Replace(text, "<", "<", -1) 2660 text = strings.Replace(text, ">", ">", -1) 2661 text = strings.Replace(text, "'", "'", -1) 2662 text = strings.Replace(text, "\"", """, -1) 2663 text = strings.Replace(text, "\"", """, -1) 2664 text = strings.Replace(text, "“", "“", -1) 2665 text = strings.Replace(text, "”", "”", -1) 2666 text = strings.Replace(text, " ", " ", -1) 2667 return text 2668 } 2669 2670 //Htmlunquote 实体符号解释为HTML 2671 func Htmlunquote(text string) string { 2672 /* 2673 Decodes `text` that's HTML quoted. 2674 >>> htmlunquote('<'&">') 2675 '<\\'&">' 2676 */ 2677 2678 // strings.Replace(s, old, new, n) 2679 // 在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换 2680 2681 text = strings.Replace(text, " ", " ", -1) 2682 text = strings.Replace(text, "”", "”", -1) 2683 text = strings.Replace(text, "“", "“", -1) 2684 text = strings.Replace(text, """, "\"", -1) 2685 text = strings.Replace(text, """, "\"", -1) 2686 text = strings.Replace(text, "'", "'", -1) 2687 text = strings.Replace(text, ">", ">", -1) 2688 text = strings.Replace(text, "<", "<", -1) 2689 text = strings.Replace(text, "…", "…", -1) 2690 text = strings.Replace(text, "&", "&", -1) // Must be done last! 2691 return text 2692 } 2693 2694 //HTML2str 过滤html文本内容中的特殊标签或内容 2695 func HTML2str(html string) string { 2696 src := string(html) 2697 2698 //替换HTML的空白字符为空格 2699 re := regexp.MustCompile(`\s`) //ns*r 2700 src = re.ReplaceAllString(src, " ") 2701 2702 //将HTML标签全转换成小写 2703 re, _ = regexp.Compile("\\<[\\S\\s]+?\\>") 2704 src = re.ReplaceAllStringFunc(src, strings.ToLower) 2705 2706 //去除STYLE 2707 re, _ = regexp.Compile("\\<style[\\S\\s]+?\\</style\\>") 2708 src = re.ReplaceAllString(src, "") 2709 2710 //去除SCRIPT 2711 re, _ = regexp.Compile("\\<script[\\S\\s]+?\\</script\\>") 2712 src = re.ReplaceAllString(src, "") 2713 2714 //去除所有尖括号内的HTML代码,并换成换行符 2715 re, _ = regexp.Compile("\\<[\\S\\s]+?\\>") 2716 src = re.ReplaceAllString(src, "\n") 2717 2718 //去除连续的换行符 2719 re, _ = regexp.Compile("\\s{2,}") 2720 src = re.ReplaceAllString(src, "\n") 2721 2722 return strings.TrimSpace(src) 2723 } 2724 2725 // Str2HTML 转换html文本为html模板对象 2726 func Str2HTML(raw string) template.HTML { 2727 return template.HTML(raw) 2728 } 2729 2730 // ObjPolicy 对用户生成内容进行过滤 2731 func ObjPolicy() *bluemonday.Policy { 2732 p := bluemonday.UGCPolicy() 2733 // Permits the "dir", "id", "lang", "title" attributes globally 2734 p.AllowStandardAttributes() 2735 2736 // Permits the "img" element and it's standard attributes 2737 p.AllowImages() 2738 2739 // Permits ordered and unordered lists, and also definition lists 2740 p.AllowLists() 2741 2742 // Permits HTML tables and all applicable elements and non-styling attributes 2743 p.AllowTables() 2744 2745 p.AllowDataURIImages() 2746 p.AllowAttrs("style").OnElements("img", "span") 2747 p.AllowStyling() 2748 2749 //视频音频标签过滤及支持 2750 p.AllowAttrs("controls", "autoplay", "loop", "preload", "autoplay").OnElements("video", "audio") 2751 p.AllowAttrs("width", "height").OnElements("video", "object", "embed") 2752 p.AllowAttrs("align").OnElements("object", "embed") 2753 p.AllowAttrs("type").Matching(regexp.MustCompile("(application/x-shockwave-flash|video/webm|video/ogg|video/mp4|video/mpeg|audio/wav|audio/mpeg|audio/x-midi)")).OnElements("object", "embed") 2754 p.AllowAttrs("quality", "allowFullScreen", "allowScriptAccess").OnElements("embed") 2755 p.AllowAttrs("src", "data").Matching(regexp.MustCompile("[a-zA-z]+://[^\\s]*(.swf|.flv|.webm|.ogg|.mp4|.mpeg|.mpg|.mpe|.wav|.mp3|.midi|.mid)")).OnElements("video", "audio", "embed", "object") 2756 p.AllowAttrs("wmode").OnElements("object", "embed") 2757 2758 //<param name='allowFullScreen' value='true'/> <param name="" value="opaque"/> 2759 p.AllowAttrs("name").Matching(regexp.MustCompile("(allowFullScreen)")).OnElements("param") 2760 p.AllowAttrs("value").Matching(regexp.MustCompile("(true)")).OnElements("param") 2761 return p 2762 } 2763 2764 //StandardURLsPolicy 返回标准URL Policy对象 2765 func StandardURLsPolicy() *bluemonday.Policy { 2766 p := bluemonday.NewPolicy() 2767 p.AllowStandardURLs() 2768 return p 2769 } 2770 2771 //PurePolicy 返回纯Policy对象 2772 func PurePolicy() *bluemonday.Policy { 2773 return bluemonday.NewPolicy() 2774 } 2775 2776 //Htm2Str 将html字符内容以纯Policy方式过滤内容后返回字符串 2777 func Htm2Str(htm string) string { 2778 p := PurePolicy() 2779 return p.Sanitize(htm) 2780 } 2781 2782 //UnixNS2Time 转换时间戳为时间字符串 单位为纳秒 2783 func UnixNS2Time(ns int64, timeLayout string) string { 2784 return time.Unix(0, ns).Format(timeLayout) 2785 //上面那行代码实际上并没神马卵用, 2786 //因在格式化的时候接受不了太高精度,显示会有问题,是个bug! 2787 } 2788 2789 //Unix2Time 转换时间戳为时间字符串 单位为秒 2790 func Unix2Time(s int64, timeLayout string) string { 2791 return time.Unix(s, 0).Format(timeLayout) 2792 } 2793 2794 //OrderKey 对key进行排序拼接 2795 func OrderKey(FUid, SUid int64) string { 2796 min := FUid 2797 max := SUid 2798 if min > max { 2799 min = SUid 2800 max = FUid 2801 } 2802 return fmt.Sprintf("%v:%v", min, max) 2803 } 2804 2805 //Compare 在模板中比较变量 2806 func Compare(f, s, t string) bool { 2807 2808 fInt, e := strconv.ParseInt(f, 10, 64) 2809 if e != nil { 2810 return false 2811 } 2812 2813 tInt, e := strconv.ParseInt(t, 10, 64) 2814 if e != nil { 2815 return false 2816 } 2817 2818 switch { 2819 case s == "<": 2820 return fInt < tInt 2821 case s == "<=": 2822 return fInt <= tInt 2823 case s == ">": 2824 return fInt > tInt 2825 case s == ">=": 2826 return fInt >= tInt 2827 case s == "!=": 2828 return fInt != tInt 2829 case s == "==": 2830 return fInt == tInt 2831 } 2832 2833 return false 2834 2835 }