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) + `/">上一页 &raquo;</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) + `/">下一页 &raquo;</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="#">&larr; Older</a>
   414  				</li>
   415  				<li class="next">
   416  					<a href="#">Newer &rarr;</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) + "'>&laquo;</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) + "'>&raquo;</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="#">&laquo;</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="#">&raquo;</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) + "'>&laquo;</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) + "'>&raquo;</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) + `/" >上一页 &raquo;</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) + `/" >下一页 &raquo;</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, "&amp;#34;", "&#34;", -1) // &#34; """
  2561  		text = strings.Replace(text, "&amp;#39;", "&#39;", -1) //&#39; '''
  2562  		text = strings.Replace(text, "&amp;lt;", "&lt;", -1)   // <
  2563  		text = strings.Replace(text, "&amp;gt;", "&gt;", -1)   // >
  2564  		text = strings.Replace(text, "&hellip;", "…", -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  				 '&lt;&#39;&amp;&quot;&gt;'
  2655  	*/
  2656  
  2657  	text = strings.Replace(text, "&", "&amp;", -1) // Must be done first!
  2658  	text = strings.Replace(text, "…", "&hellip;", -1)
  2659  	text = strings.Replace(text, "<", "&lt;", -1)
  2660  	text = strings.Replace(text, ">", "&gt;", -1)
  2661  	text = strings.Replace(text, "'", "&#39;", -1)
  2662  	text = strings.Replace(text, "\"", "&#34;", -1)
  2663  	text = strings.Replace(text, "\"", "&quot;", -1)
  2664  	text = strings.Replace(text, "“", "&ldquo;", -1)
  2665  	text = strings.Replace(text, "”", "&rdquo;", -1)
  2666  	text = strings.Replace(text, " ", "&nbsp;", -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('&lt;&#39;&amp;&quot;&gt;')
  2675  				 '<\\'&">'
  2676  	*/
  2677  
  2678  	// strings.Replace(s, old, new, n)
  2679  	// 在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换
  2680  
  2681  	text = strings.Replace(text, "&nbsp;", " ", -1)
  2682  	text = strings.Replace(text, "&rdquo;", "”", -1)
  2683  	text = strings.Replace(text, "&ldquo;", "“", -1)
  2684  	text = strings.Replace(text, "&quot;", "\"", -1)
  2685  	text = strings.Replace(text, "&#34;", "\"", -1)
  2686  	text = strings.Replace(text, "&#39;", "'", -1)
  2687  	text = strings.Replace(text, "&gt;", ">", -1)
  2688  	text = strings.Replace(text, "&lt;", "<", -1)
  2689  	text = strings.Replace(text, "&hellip;", "…", -1)
  2690  	text = strings.Replace(text, "&amp;", "&", -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  }