package main import ( "flag" "fmt" "git.aionnect.com/aionnect/go-common/utils" "git.aionnect.com/aionnect/go-common/utils/jsonutil" "github.com/tealeg/xlsx" "net/http" "net/url" "strings" "sync" ) /* 长链接批量转短链接小工具 long2short.exe 是Windows版本 long2short 是Mac版本 不是直接双击使用,需要在控制台或终端中输入命令调用 用 long2short -help 查看命令参数说明 使用步骤: 1. 准备好包含长链接的Excel文件,文件务必保存为xlsx格式 2. 准备Token,-d参数用于选择生成短链接的网站 当默认不带-d参数或参数值为1时,从微信公众平台获取短链接,问研发或管理员获取最新有效的微信公众号AccessToken 当-d参数值为2时,从mrw.so获取短链接,-t 参数传 5f18f5c344bb352d8469636b@bc07aaef03c74705fcd31248643f8bff,如果大量报错,去mrw.so网站注册获取新的开发者key 当-d参数值为3时,从url.cy获取短链接,不需要-t参数 3. 打开Windows控制台或Mac终端,执行类似下面的命令 Windows: .\long2short.exe -d 2 -i .\input.xlsx -o .\output.xlsx -h 长链接 -t AccessToken的值 Mac: ./long2short -d 2 -i ./input.xlsx -o ./output.xlsx -h 长链接 -t AccessToken的值 4. 如果出错,会在命令窗口打印错误信息,如果顺利,会在程序所在目录下输出新的xlsx文件 */ func main() { var domainType int var inputFileName string var accessToken string var headerName string var outputFileName string flag.IntVar(&domainType, "d", 1, "生成短链接的域名类型,1:url.cn 2:mrw.so 3:url.cy,默认值1") flag.StringVar(&inputFileName, "i", "input.xlsx", "xlsx输入文件路径,默认值\"input.xlsx\"") flag.StringVar(&accessToken, "t", "", "Access Token,必填") flag.StringVar(&headerName, "h", "合成链接", "长链接列名,默认值\"合成链接\"") flag.StringVar(&outputFileName, "o", "output.xlsx", "xlsx输出文件路径,会在文件名后加上时间戳,默认值\"output.xlsx\"") flag.Parse() if domainType != 3 && accessToken == "" { println("当-d参数取值为1、2时-t AccessToken 参数是必须的") return } var reqUrl string if domainType == 3 { // url.cy缩短网址 reqUrl = "https://url.cy/api/create" } else if domainType == 2 { // mrw.so缩短网址 reqUrl = fmt.Sprintf("http://mrw.so/api.php?format=json&key=%s&url=", accessToken) } else { // 微信公众平台 reqUrl = fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/shorturl?access_token=%s", accessToken) } file, err := xlsx.OpenFile(inputFileName) if err != nil { panic(fmt.Sprintf("读取输入文件%s失败 %s", inputFileName, err.Error())) } sheets := file.Sheets if nil == sheets || len(sheets) == 0 { println("输入文件中没有工作表") return } for _, sheet := range file.Sheets { rows := sheet.Rows if nil == rows || len(rows) < 2 { fmt.Printf("工作表%s中没有数据\n", sheet.Name) continue } header := rows[0] idx := -1 for i, cell := range header.Cells { text := cell.String() if strings.TrimSpace(text) == headerName { idx = i } } if idx < 0 { fmt.Printf("工作表%s中没有找到长链接列名列名\"%s\"\n", sheet.Name, headerName) continue } newHeader := header.AddCell() newHeader.SetString("短链接") length := len(rows) - 1 pNo := 1 if length > 5 { pNo = 5 } if length > 100 { pNo = 10 } if length > 500 { pNo = 100 } size := length / pNo var firstArr []*RowInfo rowGroup := [][]*RowInfo{firstArr} var j, ct int for i := 1; i < len(rows); i++ { rowGroup[j] = append(rowGroup[j], &RowInfo{Row: rows[i], RowNo: i + 1}) ct++ if ct >= size { var newArr []*RowInfo rowGroup = append(rowGroup, newArr) ct = 0 j++ } } var counter sync.WaitGroup counter.Add(length) for i := 0; i < len(rowGroup); i++ { arr := rowGroup[i] go func(domainType int, reqUrl string, idx int, sheetName string, arr []*RowInfo) { for j := 0; j < len(arr); j++ { info := arr[j] call(domainType, reqUrl, idx, sheetName, info) counter.Done() } }(domainType, reqUrl, idx, sheet.Name, arr) } counter.Wait() } dotIdx := strings.LastIndex(outputFileName, ".") outputFileName = fmt.Sprintf("%s_%d.xlsx", outputFileName[:dotIdx], utils.NextId()) err = file.Save(outputFileName) if nil != err { panic(fmt.Sprintf("写输出文件%s失败 %s", outputFileName, err.Error())) } } var httpPromise = utils.NewRequest() func call(domainType int, reqUrl string, idx int, sheetName string, info *RowInfo) { row := info.Row var longUrl string if len(row.Cells) > idx { longUrl = row.Cells[idx].String() } longUrl = strings.TrimSpace(longUrl) if longUrl == "" { fmt.Printf("工作表%s中第%d行长链接值为空\n", sheetName, info.RowNo) return } var respBody []byte var err error if domainType == 3 { // url.cy缩短网址 form := url.Values{} form.Add("url", longUrl) respBody, err = httpPromise. SetHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"). Call(http.MethodPost, reqUrl, strings.NewReader(form.Encode())) } else if domainType == 2 { // mrw.so缩短网址 reqUrl = reqUrl + url.QueryEscape(longUrl) respBody, err = httpPromise.Call(http.MethodGet, reqUrl, nil) } else { // 微信公众平台 reqBody := fmt.Sprintf(`{"action":"long2short","long_url":"%s"}`, longUrl) respBody, err = httpPromise.Call(http.MethodPost, reqUrl, strings.NewReader(reqBody)) } if nil != err { fmt.Printf("工作表%s中第%d行请求短链接失败 %s\n", sheetName, info.RowNo, err.Error()) return } var shortUrl string if domainType == 3 { // url.cy缩短网址 var res CYShortResult err = jsonutil.Unmarshal(respBody, &res) if nil != err { fmt.Printf("工作表%s中第%d行请求短链接返回了意外的结果 %s\n", sheetName, info.RowNo, string(respBody)) return } res.ShortUrl = strings.TrimSpace(res.ShortUrl) if res.ErrCode != 1 || res.ShortUrl == "" { fmt.Printf("工作表%s中第%d行请求短链接出错 %s\n", sheetName, info.RowNo, string(respBody)) return } shortUrl = res.ShortUrl } else if domainType == 2 { // mrw.so缩短网址 var res MRWShortResult err = jsonutil.Unmarshal(respBody, &res) if nil != err { fmt.Printf("工作表%s中第%d行请求短链接返回了意外的结果 %s\n", sheetName, info.RowNo, string(respBody)) return } res.ErrMsg = strings.TrimSpace(res.ErrMsg) res.ShortUrl = strings.TrimSpace(res.ShortUrl) if res.ErrMsg != "" || res.ShortUrl == "" { fmt.Printf("工作表%s中第%d行请求短链接出错 %s\n", sheetName, info.RowNo, string(respBody)) return } shortUrl = res.ShortUrl } else { // 微信公众平台 var res WXShortResult err = jsonutil.Unmarshal(respBody, &res) if nil != err { fmt.Printf("工作表%s中第%d行请求短链接返回了意外的结果 %s\n", sheetName, info.RowNo, string(respBody)) return } res.ShortUrl = strings.TrimSpace(res.ShortUrl) if res.ErrCode != 0 || res.ShortUrl == "" { fmt.Printf("工作表%s中第%d行请求短链接出错 %s\n", sheetName, info.RowNo, string(respBody)) return } shortUrl = res.ShortUrl } newCell := row.AddCell() newCell.SetString(shortUrl) } type RowInfo struct { Row *xlsx.Row RowNo int } type WXShortResult struct { ErrCode int `json:"errcode"` ErrMsg string `json:"errmsg"` ShortUrl string `json:"short_url"` } type MRWShortResult struct { ErrMsg string `json:"err"` ShortUrl string `json:"url"` } type CYShortResult struct { ErrCode int `json:"code"` ErrMsg string `json:"msg"` ShortUrl string `json:"data"` }