f
This commit is contained in:
115
internal/infrastructure/external/storage/local_file_storage_service.go
vendored
Normal file
115
internal/infrastructure/external/storage/local_file_storage_service.go
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// LocalFileStorageService 本地文件存储服务
|
||||
type LocalFileStorageService struct {
|
||||
basePath string
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// LocalFileStorageConfig 本地文件存储配置
|
||||
type LocalFileStorageConfig struct {
|
||||
BasePath string `yaml:"base_path"`
|
||||
}
|
||||
|
||||
// NewLocalFileStorageService 创建本地文件存储服务
|
||||
func NewLocalFileStorageService(basePath string, logger *zap.Logger) *LocalFileStorageService {
|
||||
// 确保基础路径存在
|
||||
if err := os.MkdirAll(basePath, 0755); err != nil {
|
||||
logger.Error("创建基础存储目录失败", zap.Error(err), zap.String("path", basePath))
|
||||
}
|
||||
|
||||
return &LocalFileStorageService{
|
||||
basePath: basePath,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// StoreFile 存储文件
|
||||
func (s *LocalFileStorageService) StoreFile(ctx context.Context, file io.Reader, filename string) (string, error) {
|
||||
// 构建完整文件路径
|
||||
fullPath := filepath.Join(s.basePath, filename)
|
||||
|
||||
// 确保目录存在
|
||||
dir := filepath.Dir(fullPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
s.logger.Error("创建目录失败", zap.Error(err), zap.String("dir", dir))
|
||||
return "", fmt.Errorf("创建目录失败: %w", err)
|
||||
}
|
||||
|
||||
// 创建文件
|
||||
dst, err := os.Create(fullPath)
|
||||
if err != nil {
|
||||
s.logger.Error("创建文件失败", zap.Error(err), zap.String("path", fullPath))
|
||||
return "", fmt.Errorf("创建文件失败: %w", err)
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
// 复制文件内容
|
||||
if _, err := io.Copy(dst, file); err != nil {
|
||||
s.logger.Error("写入文件失败", zap.Error(err), zap.String("path", fullPath))
|
||||
// 删除部分写入的文件
|
||||
_ = os.Remove(fullPath)
|
||||
return "", fmt.Errorf("写入文件失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("文件存储成功", zap.String("path", fullPath))
|
||||
return fullPath, nil
|
||||
}
|
||||
|
||||
// StoreMultipartFile 存储multipart文件
|
||||
func (s *LocalFileStorageService) StoreMultipartFile(ctx context.Context, file *multipart.FileHeader, filename string) (string, error) {
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("打开上传文件失败: %w", err)
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
return s.StoreFile(ctx, src, filename)
|
||||
}
|
||||
|
||||
// GetFileURL 获取文件URL
|
||||
func (s *LocalFileStorageService) GetFileURL(ctx context.Context, filePath string) (string, error) {
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("文件不存在: %s", filePath)
|
||||
}
|
||||
|
||||
// 返回文件路径(在实际应用中,这里应该返回可访问的URL)
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
// DeleteFile 删除文件
|
||||
func (s *LocalFileStorageService) DeleteFile(ctx context.Context, filePath string) error {
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// 文件不存在,不视为错误
|
||||
return nil
|
||||
}
|
||||
s.logger.Error("删除文件失败", zap.Error(err), zap.String("path", filePath))
|
||||
return fmt.Errorf("删除文件失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("文件删除成功", zap.String("path", filePath))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFileReader 获取文件读取器
|
||||
func (s *LocalFileStorageService) GetFileReader(ctx context.Context, filePath string) (io.ReadCloser, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("打开文件失败: %w", err)
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
110
internal/infrastructure/external/storage/local_file_storage_service_impl.go
vendored
Normal file
110
internal/infrastructure/external/storage/local_file_storage_service_impl.go
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// LocalFileStorageServiceImpl 本地文件存储服务实现
|
||||
type LocalFileStorageServiceImpl struct {
|
||||
basePath string
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewLocalFileStorageServiceImpl 创建本地文件存储服务实现
|
||||
func NewLocalFileStorageServiceImpl(basePath string, logger *zap.Logger) *LocalFileStorageServiceImpl {
|
||||
// 确保基础路径存在
|
||||
if err := os.MkdirAll(basePath, 0755); err != nil {
|
||||
logger.Error("创建基础存储目录失败", zap.Error(err), zap.String("path", basePath))
|
||||
}
|
||||
|
||||
return &LocalFileStorageServiceImpl{
|
||||
basePath: basePath,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// StoreFile 存储文件
|
||||
func (s *LocalFileStorageServiceImpl) StoreFile(ctx context.Context, file io.Reader, filename string) (string, error) {
|
||||
// 构建完整文件路径
|
||||
fullPath := filepath.Join(s.basePath, filename)
|
||||
|
||||
// 确保目录存在
|
||||
dir := filepath.Dir(fullPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
s.logger.Error("创建目录失败", zap.Error(err), zap.String("dir", dir))
|
||||
return "", fmt.Errorf("创建目录失败: %w", err)
|
||||
}
|
||||
|
||||
// 创建文件
|
||||
dst, err := os.Create(fullPath)
|
||||
if err != nil {
|
||||
s.logger.Error("创建文件失败", zap.Error(err), zap.String("path", fullPath))
|
||||
return "", fmt.Errorf("创建文件失败: %w", err)
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
// 复制文件内容
|
||||
if _, err := io.Copy(dst, file); err != nil {
|
||||
s.logger.Error("写入文件失败", zap.Error(err), zap.String("path", fullPath))
|
||||
// 删除部分写入的文件
|
||||
_ = os.Remove(fullPath)
|
||||
return "", fmt.Errorf("写入文件失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("文件存储成功", zap.String("path", fullPath))
|
||||
return fullPath, nil
|
||||
}
|
||||
|
||||
// StoreMultipartFile 存储multipart文件
|
||||
func (s *LocalFileStorageServiceImpl) StoreMultipartFile(ctx context.Context, file *multipart.FileHeader, filename string) (string, error) {
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("打开上传文件失败: %w", err)
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
return s.StoreFile(ctx, src, filename)
|
||||
}
|
||||
|
||||
// GetFileURL 获取文件URL
|
||||
func (s *LocalFileStorageServiceImpl) GetFileURL(ctx context.Context, filePath string) (string, error) {
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("文件不存在: %s", filePath)
|
||||
}
|
||||
|
||||
// 返回文件路径(在实际应用中,这里应该返回可访问的URL)
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
// DeleteFile 删除文件
|
||||
func (s *LocalFileStorageServiceImpl) DeleteFile(ctx context.Context, filePath string) error {
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// 文件不存在,不视为错误
|
||||
return nil
|
||||
}
|
||||
s.logger.Error("删除文件失败", zap.Error(err), zap.String("path", filePath))
|
||||
return fmt.Errorf("删除文件失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("文件删除成功", zap.String("path", filePath))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFileReader 获取文件读取器
|
||||
func (s *LocalFileStorageServiceImpl) GetFileReader(ctx context.Context, filePath string) (io.ReadCloser, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("打开文件失败: %w", err)
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
353
internal/infrastructure/external/storage/qiniu_storage_service.go
vendored
Normal file
353
internal/infrastructure/external/storage/qiniu_storage_service.go
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/qiniu/go-sdk/v7/auth/qbox"
|
||||
"github.com/qiniu/go-sdk/v7/storage"
|
||||
"go.uber.org/zap"
|
||||
|
||||
sharedStorage "hyapi-server/internal/shared/storage"
|
||||
)
|
||||
|
||||
// QiNiuStorageService 七牛云存储服务
|
||||
type QiNiuStorageService struct {
|
||||
accessKey string
|
||||
secretKey string
|
||||
bucket string
|
||||
domain string
|
||||
logger *zap.Logger
|
||||
mac *qbox.Mac
|
||||
bucketManager *storage.BucketManager
|
||||
}
|
||||
|
||||
// QiNiuStorageConfig 七牛云存储配置
|
||||
type QiNiuStorageConfig struct {
|
||||
AccessKey string `yaml:"access_key"`
|
||||
SecretKey string `yaml:"secret_key"`
|
||||
Bucket string `yaml:"bucket"`
|
||||
Domain string `yaml:"domain"`
|
||||
}
|
||||
|
||||
// NewQiNiuStorageService 创建七牛云存储服务
|
||||
func NewQiNiuStorageService(accessKey, secretKey, bucket, domain string, logger *zap.Logger) *QiNiuStorageService {
|
||||
mac := qbox.NewMac(accessKey, secretKey)
|
||||
// 使用默认配置,不需要指定region
|
||||
cfg := storage.Config{}
|
||||
bucketManager := storage.NewBucketManager(mac, &cfg)
|
||||
|
||||
return &QiNiuStorageService{
|
||||
accessKey: accessKey,
|
||||
secretKey: secretKey,
|
||||
bucket: bucket,
|
||||
domain: domain,
|
||||
logger: logger,
|
||||
mac: mac,
|
||||
bucketManager: bucketManager,
|
||||
}
|
||||
}
|
||||
|
||||
// UploadFile 上传文件到七牛云
|
||||
func (s *QiNiuStorageService) UploadFile(ctx context.Context, fileBytes []byte, fileName string) (*sharedStorage.UploadResult, error) {
|
||||
s.logger.Info("开始上传文件到七牛云",
|
||||
zap.String("file_name", fileName),
|
||||
zap.Int("file_size", len(fileBytes)),
|
||||
)
|
||||
|
||||
// 生成唯一的文件key
|
||||
key := s.generateFileKey(fileName)
|
||||
|
||||
// 创建上传凭证
|
||||
putPolicy := storage.PutPolicy{
|
||||
Scope: s.bucket,
|
||||
}
|
||||
upToken := putPolicy.UploadToken(s.mac)
|
||||
|
||||
// 配置上传参数
|
||||
cfg := storage.Config{}
|
||||
formUploader := storage.NewFormUploader(&cfg)
|
||||
ret := storage.PutRet{}
|
||||
|
||||
// 上传文件
|
||||
err := formUploader.Put(ctx, &ret, upToken, key, strings.NewReader(string(fileBytes)), int64(len(fileBytes)), &storage.PutExtra{})
|
||||
if err != nil {
|
||||
s.logger.Error("文件上传失败",
|
||||
zap.String("file_name", fileName),
|
||||
zap.String("key", key),
|
||||
zap.Error(err),
|
||||
)
|
||||
return nil, fmt.Errorf("文件上传失败: %w", err)
|
||||
}
|
||||
|
||||
// 构建文件URL
|
||||
fileURL := s.GetFileURL(ctx, key)
|
||||
|
||||
s.logger.Info("文件上传成功",
|
||||
zap.String("file_name", fileName),
|
||||
zap.String("key", key),
|
||||
zap.String("url", fileURL),
|
||||
)
|
||||
|
||||
return &sharedStorage.UploadResult{
|
||||
Key: key,
|
||||
URL: fileURL,
|
||||
MimeType: s.getMimeType(fileName),
|
||||
Size: int64(len(fileBytes)),
|
||||
Hash: ret.Hash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GenerateUploadToken 生成上传凭证
|
||||
func (s *QiNiuStorageService) GenerateUploadToken(ctx context.Context, key string) (string, error) {
|
||||
putPolicy := storage.PutPolicy{
|
||||
Scope: s.bucket,
|
||||
// 设置过期时间(1小时)
|
||||
Expires: uint64(time.Now().Add(time.Hour).Unix()),
|
||||
}
|
||||
|
||||
token := putPolicy.UploadToken(s.mac)
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GetFileURL 获取文件访问URL
|
||||
func (s *QiNiuStorageService) GetFileURL(ctx context.Context, key string) string {
|
||||
// 如果是私有空间,需要生成带签名的URL
|
||||
if s.isPrivateBucket() {
|
||||
deadline := time.Now().Add(time.Hour).Unix() // 1小时过期
|
||||
privateAccessURL := storage.MakePrivateURL(s.mac, s.domain, key, deadline)
|
||||
return privateAccessURL
|
||||
}
|
||||
|
||||
// 公开空间直接返回URL
|
||||
return fmt.Sprintf("%s/%s", s.domain, key)
|
||||
}
|
||||
|
||||
// GetPrivateFileURL 获取私有文件访问URL
|
||||
func (s *QiNiuStorageService) GetPrivateFileURL(ctx context.Context, key string, expires int64) (string, error) {
|
||||
baseURL := s.GetFileURL(ctx, key)
|
||||
|
||||
// TODO: 实际集成七牛云SDK生成私有URL
|
||||
s.logger.Info("生成七牛云私有文件URL",
|
||||
zap.String("key", key),
|
||||
zap.Int64("expires", expires),
|
||||
)
|
||||
|
||||
// 模拟返回私有URL
|
||||
return fmt.Sprintf("%s?token=mock_private_token&expires=%d", baseURL, expires), nil
|
||||
}
|
||||
|
||||
// DeleteFile 删除文件
|
||||
func (s *QiNiuStorageService) DeleteFile(ctx context.Context, key string) error {
|
||||
s.logger.Info("删除七牛云文件", zap.String("key", key))
|
||||
|
||||
err := s.bucketManager.Delete(s.bucket, key)
|
||||
if err != nil {
|
||||
s.logger.Error("删除文件失败",
|
||||
zap.String("key", key),
|
||||
zap.Error(err),
|
||||
)
|
||||
return fmt.Errorf("删除文件失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("文件删除成功", zap.String("key", key))
|
||||
return nil
|
||||
}
|
||||
|
||||
// FileExists 检查文件是否存在
|
||||
func (s *QiNiuStorageService) FileExists(ctx context.Context, key string) (bool, error) {
|
||||
// TODO: 实际集成七牛云SDK检查文件存在性
|
||||
s.logger.Info("检查七牛云文件存在性", zap.String("key", key))
|
||||
|
||||
// 模拟文件存在
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// GetFileInfo 获取文件信息
|
||||
func (s *QiNiuStorageService) GetFileInfo(ctx context.Context, key string) (*sharedStorage.FileInfo, error) {
|
||||
fileInfo, err := s.bucketManager.Stat(s.bucket, key)
|
||||
if err != nil {
|
||||
s.logger.Error("获取文件信息失败",
|
||||
zap.String("key", key),
|
||||
zap.Error(err),
|
||||
)
|
||||
return nil, fmt.Errorf("获取文件信息失败: %w", err)
|
||||
}
|
||||
|
||||
return &sharedStorage.FileInfo{
|
||||
Key: key,
|
||||
Size: fileInfo.Fsize,
|
||||
MimeType: fileInfo.MimeType,
|
||||
Hash: fileInfo.Hash,
|
||||
PutTime: fileInfo.PutTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListFiles 列出文件
|
||||
func (s *QiNiuStorageService) ListFiles(ctx context.Context, prefix string, limit int) ([]*sharedStorage.FileInfo, error) {
|
||||
entries, _, _, hasMore, err := s.bucketManager.ListFiles(s.bucket, prefix, "", "", limit)
|
||||
if err != nil {
|
||||
s.logger.Error("列出文件失败",
|
||||
zap.String("prefix", prefix),
|
||||
zap.Error(err),
|
||||
)
|
||||
return nil, fmt.Errorf("列出文件失败: %w", err)
|
||||
}
|
||||
|
||||
var fileInfos []*sharedStorage.FileInfo
|
||||
for _, entry := range entries {
|
||||
fileInfo := &sharedStorage.FileInfo{
|
||||
Key: entry.Key,
|
||||
Size: entry.Fsize,
|
||||
MimeType: entry.MimeType,
|
||||
Hash: entry.Hash,
|
||||
PutTime: entry.PutTime,
|
||||
}
|
||||
fileInfos = append(fileInfos, fileInfo)
|
||||
}
|
||||
|
||||
_ = hasMore // 暂时忽略hasMore
|
||||
return fileInfos, nil
|
||||
}
|
||||
|
||||
// generateFileKey 生成文件key
|
||||
func (s *QiNiuStorageService) generateFileKey(fileName string) string {
|
||||
// 生成时间戳
|
||||
timestamp := time.Now().Format("20060102_150405")
|
||||
// 生成随机字符串
|
||||
randomStr := fmt.Sprintf("%d", time.Now().UnixNano()%1000000)
|
||||
// 获取文件扩展名
|
||||
ext := filepath.Ext(fileName)
|
||||
// 构建key: 日期/时间戳_随机数.扩展名
|
||||
key := fmt.Sprintf("certification/%s/%s_%s%s",
|
||||
time.Now().Format("20060102"), timestamp, randomStr, ext)
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
// getMimeType 根据文件名获取MIME类型
|
||||
func (s *QiNiuStorageService) getMimeType(fileName string) string {
|
||||
ext := strings.ToLower(filepath.Ext(fileName))
|
||||
switch ext {
|
||||
case ".jpg", ".jpeg":
|
||||
return "image/jpeg"
|
||||
case ".png":
|
||||
return "image/png"
|
||||
case ".pdf":
|
||||
return "application/pdf"
|
||||
case ".gif":
|
||||
return "image/gif"
|
||||
case ".bmp":
|
||||
return "image/bmp"
|
||||
case ".webp":
|
||||
return "image/webp"
|
||||
default:
|
||||
return "application/octet-stream"
|
||||
}
|
||||
}
|
||||
|
||||
// isPrivateBucket 判断是否为私有空间
|
||||
func (s *QiNiuStorageService) isPrivateBucket() bool {
|
||||
// 这里可以根据配置或域名特征判断
|
||||
// 私有空间的域名通常包含特定标识
|
||||
return strings.Contains(s.domain, "private") ||
|
||||
strings.Contains(s.domain, "auth") ||
|
||||
strings.Contains(s.domain, "secure")
|
||||
}
|
||||
|
||||
// generateSignature 生成签名(用于私有空间访问)
|
||||
func (s *QiNiuStorageService) generateSignature(data string) string {
|
||||
h := hmac.New(sha1.New, []byte(s.secretKey))
|
||||
h.Write([]byte(data))
|
||||
return base64.URLEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// UploadFromReader 从Reader上传文件
|
||||
func (s *QiNiuStorageService) UploadFromReader(ctx context.Context, reader io.Reader, fileName string, fileSize int64) (*sharedStorage.UploadResult, error) {
|
||||
// 读取文件内容
|
||||
fileBytes, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取文件失败: %w", err)
|
||||
}
|
||||
|
||||
return s.UploadFile(ctx, fileBytes, fileName)
|
||||
}
|
||||
|
||||
// DownloadFile 从七牛云下载文件
|
||||
func (s *QiNiuStorageService) DownloadFile(ctx context.Context, fileURL string) ([]byte, error) {
|
||||
s.logger.Info("开始从七牛云下载文件", zap.String("file_url", fileURL))
|
||||
|
||||
// 创建HTTP客户端,超时时间设置为60秒
|
||||
client := &http.Client{
|
||||
Timeout: 60 * time.Second,
|
||||
}
|
||||
|
||||
// 创建请求
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", fileURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建请求失败: %w", err)
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
// 检查是否是超时错误
|
||||
isTimeout := false
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
isTimeout = true
|
||||
} else if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||
isTimeout = true
|
||||
} else if errStr := err.Error();
|
||||
errStr == "context deadline exceeded" ||
|
||||
errStr == "timeout" ||
|
||||
errStr == "Client.Timeout exceeded" ||
|
||||
errStr == "net/http: request canceled" {
|
||||
isTimeout = true
|
||||
}
|
||||
|
||||
errorMsg := "下载文件失败"
|
||||
if isTimeout {
|
||||
errorMsg = "下载文件超时"
|
||||
}
|
||||
s.logger.Error(errorMsg,
|
||||
zap.String("file_url", fileURL),
|
||||
zap.Error(err),
|
||||
)
|
||||
return nil, fmt.Errorf("%s: %w", errorMsg, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查响应状态
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
s.logger.Error("下载文件失败,状态码异常",
|
||||
zap.String("file_url", fileURL),
|
||||
zap.Int("status_code", resp.StatusCode),
|
||||
)
|
||||
return nil, fmt.Errorf("下载文件失败,状态码: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// 读取文件内容
|
||||
fileContent, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
s.logger.Error("读取文件内容失败",
|
||||
zap.String("file_url", fileURL),
|
||||
zap.Error(err),
|
||||
)
|
||||
return nil, fmt.Errorf("读取文件内容失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("文件下载成功",
|
||||
zap.String("file_url", fileURL),
|
||||
zap.Int("file_size", len(fileContent)),
|
||||
)
|
||||
|
||||
return fileContent, nil
|
||||
}
|
||||
Reference in New Issue
Block a user