修改文件上传as
This commit is contained in:
parent
55df08b314
commit
188d5c27b2
315
pkg/service/cast/ayrshare.go
Normal file
315
pkg/service/cast/ayrshare.go
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
package cast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"fonchain-fiee/pkg/utils"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Ayrshare API 配置
|
||||||
|
ayrshareAPIBaseURL = "https://api.ayrshare.com/api"
|
||||||
|
maxSmallFileSize = 30 * 1024 * 1024 // 30MB
|
||||||
|
apiKey = "208CBE9F-8E4F426A-990A0184-2C6287B9"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UploadMediaResponse 上传媒体文件响应
|
||||||
|
type UploadMediaResponse struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
FileName string `json:"fileName"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IsAs bool `json:"isAs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadURLResponse 获取上传URL响应
|
||||||
|
type UploadURLResponse struct {
|
||||||
|
AccessURL string `json:"accessUrl"`
|
||||||
|
ContentType string `json:"contentType"`
|
||||||
|
UploadURL string `json:"uploadUrl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyURLResponse 验证URL响应
|
||||||
|
type VerifyURLResponse struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
StatusCode int `json:"statusCode"`
|
||||||
|
ContentType string `json:"contentType"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadMediaByURL 根据文件大小自动选择上传方式
|
||||||
|
// fileURL: 文件的URL链接
|
||||||
|
// apiKey: Ayrshare API Key
|
||||||
|
// fileName: 文件名(可选)
|
||||||
|
// description: 文件描述(可选)
|
||||||
|
func UploadMediaByURL(ctx context.Context, fileURL, fileName, description string) (*UploadMediaResponse, error) {
|
||||||
|
if fileURL == "" {
|
||||||
|
return nil, errors.New("文件URL不能为空")
|
||||||
|
}
|
||||||
|
ok, err := VerifyMediaURL(ctx, fileURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
return &UploadMediaResponse{
|
||||||
|
ID: "",
|
||||||
|
URL: fileURL,
|
||||||
|
FileName: "",
|
||||||
|
Description: "",
|
||||||
|
IsAs: false,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
// 下载文件到临时目录
|
||||||
|
tempFile, fileSize, err := downloadFile(ctx, fileURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("下载文件失败: %v", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tempFile) // 清理临时文件
|
||||||
|
|
||||||
|
zap.L().Info("文件下载完成", zap.String("tempFile", tempFile), zap.Int64("fileSize", fileSize))
|
||||||
|
|
||||||
|
// 如果没有提供文件名,从URL中提取
|
||||||
|
if fileName == "" {
|
||||||
|
fileName = filepath.Base(fileURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据文件大小选择上传方式
|
||||||
|
if fileSize < maxSmallFileSize {
|
||||||
|
zap.L().Info("使用小文件上传方式", zap.Int64("fileSize", fileSize))
|
||||||
|
return uploadSmallMedia(ctx, tempFile, apiKey, fileName, description)
|
||||||
|
}
|
||||||
|
zap.L().Info("使用大文件上传方式", zap.Int64("fileSize", fileSize))
|
||||||
|
return uploadLargeMedia(ctx, tempFile, apiKey, fileName, description)
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadFile 下载文件到临时目录
|
||||||
|
func downloadFile(ctx context.Context, fileURL string) (string, int64, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", fileURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: 10 * time.Minute}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return "", 0, fmt.Errorf("下载文件失败,HTTP状态码: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建临时文件
|
||||||
|
tempFile, err := os.CreateTemp("", "ayrshare_upload_*")
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
defer tempFile.Close()
|
||||||
|
|
||||||
|
// 复制内容到临时文件
|
||||||
|
fileSize, err := io.Copy(tempFile, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
os.Remove(tempFile.Name())
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempFile.Name(), fileSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// uploadSmallMedia 上传小于30MB的文件(使用multipart form-data)
|
||||||
|
func uploadSmallMedia(ctx context.Context, filePath, apiKey, fileName, description string) (*UploadMediaResponse, error) {
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("打开文件失败: %v", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// 创建multipart form-data
|
||||||
|
body := &bytes.Buffer{}
|
||||||
|
writer := multipart.NewWriter(body)
|
||||||
|
|
||||||
|
// 添加文件
|
||||||
|
part, err := writer.CreateFormFile("file", fileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(part, file); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加其他字段
|
||||||
|
if fileName != "" {
|
||||||
|
_ = writer.WriteField("fileName", fileName)
|
||||||
|
}
|
||||||
|
if description != "" {
|
||||||
|
_ = writer.WriteField("description", description)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
url := fmt.Sprintf("%s/media/upload", ayrshareAPIBaseURL)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
|
||||||
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: 10 * time.Minute}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("请求失败: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
zap.L().Error("上传小文件失败", zap.Int("statusCode", resp.StatusCode), zap.String("response", string(respBody)))
|
||||||
|
return nil, fmt.Errorf("上传失败: HTTP %d, %s", resp.StatusCode, string(respBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
var result UploadMediaResponse
|
||||||
|
if err := json.Unmarshal(respBody, &result); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zap.L().Info("小文件上传成功", zap.Any("response", result))
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// uploadLargeMedia 上传大于30MB的文件(使用presigned URL)
|
||||||
|
func uploadLargeMedia(ctx context.Context, filePath, apiKey, fileName, description string) (*UploadMediaResponse, error) {
|
||||||
|
// 获取文件的content type
|
||||||
|
ext := filepath.Ext(fileName)
|
||||||
|
if len(ext) > 0 {
|
||||||
|
ext = ext[1:] // 去掉点号
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: 获取上传URL
|
||||||
|
uploadURLResp, err := getUploadURL(ctx, apiKey, fileName, ext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("获取上传URL失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zap.L().Info("获取上传URL成功", zap.Any("uploadURLResp", uploadURLResp))
|
||||||
|
|
||||||
|
// Step 2: 上传文件到presigned URL
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("打开文件失败: %v", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "PUT", uploadURLResp.UploadURL, file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", uploadURLResp.ContentType)
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: 30 * time.Minute}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("上传文件失败: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
respBody, _ := io.ReadAll(resp.Body)
|
||||||
|
zap.L().Error("上传大文件失败", zap.Int("statusCode", resp.StatusCode), zap.String("response", string(respBody)))
|
||||||
|
return nil, fmt.Errorf("上传失败: HTTP %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
zap.L().Info("大文件上传成功")
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return &UploadMediaResponse{
|
||||||
|
URL: uploadURLResp.AccessURL,
|
||||||
|
FileName: fileName,
|
||||||
|
Description: description,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUploadURL 获取大文件上传的presigned URL
|
||||||
|
func getUploadURL(ctx context.Context, apiKey, fileName, contentType string) (*UploadURLResponse, error) {
|
||||||
|
url := fmt.Sprintf("%s/media/uploadUrl?fileName=%s", ayrshareAPIBaseURL, fileName)
|
||||||
|
if contentType != "" {
|
||||||
|
url = fmt.Sprintf("%s&contentType=%s", url, contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: 30 * time.Second}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
zap.L().Error("获取上传URL失败", zap.Int("statusCode", resp.StatusCode), zap.String("response", string(respBody)))
|
||||||
|
return nil, fmt.Errorf("获取上传URL失败: HTTP %d, %s", resp.StatusCode, string(respBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
var result UploadURLResponse
|
||||||
|
if err := json.Unmarshal(respBody, &result); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyMediaURL 验证媒体URL是否有效
|
||||||
|
func VerifyMediaURL(ctx context.Context, mediaURL string) (bool, error) {
|
||||||
|
url := fmt.Sprintf("%s/media/urlExists", ayrshareAPIBaseURL)
|
||||||
|
requestBody := map[string]string{
|
||||||
|
"mediaUrl": mediaURL,
|
||||||
|
}
|
||||||
|
jsonData, _ := json.Marshal(requestBody)
|
||||||
|
code, postBody, err := utils.PostBytesHeader(url, map[string]interface{}{
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": "Bearer " + apiKey,
|
||||||
|
}, jsonData)
|
||||||
|
if err != nil {
|
||||||
|
zap.L().Error("VerifyMediaURL 提交异常", zap.Error(err))
|
||||||
|
return false, errors.New("验证链接提交异常")
|
||||||
|
}
|
||||||
|
if code != http.StatusOK {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
var result *VerifyURLResponse
|
||||||
|
_ = json.Unmarshal(postBody, &result)
|
||||||
|
if result.StatusCode != http.StatusOK || result.Status != "success" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
@ -99,6 +99,26 @@ func Test(ctx *gin.Context) {
|
|||||||
service.Success(ctx, resp)
|
service.Success(ctx, resp)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if action == "upload" {
|
||||||
|
fileUrl := ctx.PostForm("fileUrl")
|
||||||
|
uploadResp, err := UploadMediaByURL(ctx, fileUrl, "", "")
|
||||||
|
if err != nil {
|
||||||
|
service.Error(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
service.Success(ctx, uploadResp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if action == "getUrl" {
|
||||||
|
fileUrl := ctx.PostForm("fileUrl")
|
||||||
|
uploadResp, err := VerifyMediaURL(ctx, fileUrl)
|
||||||
|
if err != nil {
|
||||||
|
service.Error(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
service.Success(ctx, uploadResp)
|
||||||
|
return
|
||||||
|
}
|
||||||
service.Success(ctx, "unknow")
|
service.Success(ctx, "unknow")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -353,11 +353,37 @@ func PostAS(workUuids []string) (errs []error) {
|
|||||||
var isVideo bool
|
var isVideo bool
|
||||||
if workDetail.WorkCategory == 1 {
|
if workDetail.WorkCategory == 1 {
|
||||||
isVideo = false
|
isVideo = false
|
||||||
mediaUrls = workDetail.Images
|
for _, imageUrl := range workDetail.Images {
|
||||||
|
urlResp, err := UploadMediaByURL(context.Background(), imageUrl, "", "")
|
||||||
|
if err != nil {
|
||||||
|
zap.L().Error("Publish UploadMediaByURL failed", zap.String("imageUrl", imageUrl), zap.Error(err))
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
mediaUrls = append(mediaUrls, urlResp.URL)
|
||||||
|
}
|
||||||
|
//mediaUrls = workDetail.Images
|
||||||
|
}
|
||||||
|
var coverUrl string
|
||||||
if workDetail.WorkCategory == 2 {
|
if workDetail.WorkCategory == 2 {
|
||||||
isVideo = true
|
isVideo = true
|
||||||
mediaUrls = []string{workDetail.VideoUrl}
|
//mediaUrls = []string{workDetail.VideoUrl}
|
||||||
|
urlResp, err := UploadMediaByURL(context.Background(), workDetail.VideoUrl, "", "")
|
||||||
|
if err != nil {
|
||||||
|
zap.L().Error("Publish UploadMediaByURL failed", zap.String("VideoUrl", workDetail.VideoUrl), zap.Error(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mediaUrls = []string{urlResp.URL}
|
||||||
|
if workDetail.CoverUrl != "" {
|
||||||
|
urlResp, err = UploadMediaByURL(context.Background(), workDetail.VideoUrl, "", "")
|
||||||
|
if err != nil {
|
||||||
|
zap.L().Error("Publish UploadMediaByURL failed", zap.String("VideoUrl", workDetail.VideoUrl), zap.Error(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
coverUrl = urlResp.URL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(mediaUrls) == 0 {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
ArtistInfoResp, _err := service.CastProvider.GetArtist(context.Background(), &cast.GetArtistReq{
|
ArtistInfoResp, _err := service.CastProvider.GetArtist(context.Background(), &cast.GetArtistReq{
|
||||||
ArtistUuid: workDetail.ArtistUuid,
|
ArtistUuid: workDetail.ArtistUuid,
|
||||||
@ -370,7 +396,6 @@ func PostAS(workUuids []string) (errs []error) {
|
|||||||
errs = append(errs, errors.New("艺人平台信息未配置"))
|
errs = append(errs, errors.New("艺人平台信息未配置"))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, platformID := range needPlatformIDs {
|
for _, platformID := range needPlatformIDs {
|
||||||
var postResp *aryshare.PostResponse = &aryshare.PostResponse{}
|
var postResp *aryshare.PostResponse = &aryshare.PostResponse{}
|
||||||
postReq := &aryshare.PostRequest{
|
postReq := &aryshare.PostRequest{
|
||||||
@ -389,7 +414,7 @@ func PostAS(workUuids []string) (errs []error) {
|
|||||||
postReq.InstagramOptions = &aryshare.InstagramOptions{
|
postReq.InstagramOptions = &aryshare.InstagramOptions{
|
||||||
ShareReelsFeed: false,
|
ShareReelsFeed: false,
|
||||||
AudioName: "",
|
AudioName: "",
|
||||||
ThumbNail: workDetail.CoverUrl,
|
ThumbNail: coverUrl,
|
||||||
ThumbNailOffset: 0,
|
ThumbNailOffset: 0,
|
||||||
Stories: false,
|
Stories: false,
|
||||||
AltText: nil,
|
AltText: nil,
|
||||||
|
|||||||
44
pkg/utils/files.go
Normal file
44
pkg/utils/files.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"fonchain-fiee/pkg/e"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SaveUrlFileDisk 保存图片到本地
|
||||||
|
func SaveUrlFileDisk(url string, path string, filename string) (fullPath string, err error) {
|
||||||
|
if err = CreateDirPath(path); err != nil {
|
||||||
|
zap.L().Error("SaveUrlFileDisk err ", zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if filename == "" {
|
||||||
|
stepName := strings.Split(url, "/")
|
||||||
|
if len(stepName) > 1 {
|
||||||
|
filename = stepName[len(stepName)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.New(e.GetMsg(e.ERROR_DOWNLOAD_FILE))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
}()
|
||||||
|
bytes, err := io.ReadAll(resp.Body)
|
||||||
|
fullPath = fmt.Sprintf("%s/%s", path, filename)
|
||||||
|
// 写入数据
|
||||||
|
err = os.WriteFile(fullPath, bytes, 0777)
|
||||||
|
return
|
||||||
|
}
|
||||||
@ -133,3 +133,24 @@ func GetUrl(apiUrl string, headerData map[string]string, proxyURL ...string) (st
|
|||||||
zap.L().Info("Get", zap.Any("url", apiUrl), zap.Any("body", body))
|
zap.L().Info("Get", zap.Any("url", apiUrl), zap.Any("body", body))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PostBytesHeader(url string, header map[string]interface{}, data []byte) (int, []byte, error) {
|
||||||
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("创建请求失败: %v", "")
|
||||||
|
}
|
||||||
|
for k, v := range header {
|
||||||
|
req.Header.Set(k, fmt.Sprintf("%v", v))
|
||||||
|
}
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("请求失败: %v", "")
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("读取响应失败: %v", "")
|
||||||
|
}
|
||||||
|
return resp.StatusCode, body, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -163,3 +164,11 @@ func CopyFile(src, dstDir string) (string, error) {
|
|||||||
}
|
}
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FileNameFromURL(rawURL string) (string, error) {
|
||||||
|
u, err := url.Parse(rawURL)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return path.Base(u.Path), nil
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user