Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
const defaultBackupPartSizeBytes int64 = 4 * 1024 * 1024 * 1024
|
||||
|
||||
// BackupPart 描述一个 gzip 字节分卷。
|
||||
type BackupPart struct {
|
||||
Index int `json:"index"`
|
||||
S3Key string `json:"s3_key"`
|
||||
SizeBytes int64 `json:"size_bytes"`
|
||||
SHA256 string `json:"sha256,omitempty"`
|
||||
}
|
||||
|
||||
type localBackupPart struct {
|
||||
Index int
|
||||
Path string
|
||||
SizeBytes int64
|
||||
SHA256 string
|
||||
}
|
||||
|
||||
func splitBackupFile(srcPath string, partSize int64) (parts []localBackupPart, err error) {
|
||||
if partSize <= 0 {
|
||||
return nil, fmt.Errorf("backup part size must be positive")
|
||||
}
|
||||
|
||||
src, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open backup archive: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if closeErr := src.Close(); err == nil && closeErr != nil {
|
||||
err = fmt.Errorf("close backup archive: %w", closeErr)
|
||||
}
|
||||
if err != nil {
|
||||
paths := make([]string, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
paths = append(paths, part.Path)
|
||||
}
|
||||
_ = cleanupBackupFiles(paths...)
|
||||
}
|
||||
}()
|
||||
|
||||
info, err := src.Stat()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("stat backup archive: %w", err)
|
||||
}
|
||||
if info.Size() <= 0 {
|
||||
return nil, errors.New("backup archive is empty")
|
||||
}
|
||||
|
||||
remaining := info.Size()
|
||||
for index := 1; remaining > 0; index++ {
|
||||
partFile, createErr := os.CreateTemp("", "sub2api-backup-part-*")
|
||||
if createErr != nil {
|
||||
return nil, fmt.Errorf("create backup part: %w", createErr)
|
||||
}
|
||||
partPath := partFile.Name()
|
||||
partBytes := partSize
|
||||
if remaining < partBytes {
|
||||
partBytes = remaining
|
||||
}
|
||||
|
||||
hash := sha256.New()
|
||||
written, copyErr := io.CopyN(io.MultiWriter(partFile, hash), src, partBytes)
|
||||
closeErr := partFile.Close()
|
||||
if copyErr != nil {
|
||||
_ = os.Remove(partPath)
|
||||
return nil, fmt.Errorf("write backup part %d: %w", index, copyErr)
|
||||
}
|
||||
if closeErr != nil {
|
||||
_ = os.Remove(partPath)
|
||||
return nil, fmt.Errorf("close backup part %d: %w", index, closeErr)
|
||||
}
|
||||
|
||||
parts = append(parts, localBackupPart{
|
||||
Index: index,
|
||||
Path: partPath,
|
||||
SizeBytes: written,
|
||||
SHA256: hex.EncodeToString(hash.Sum(nil)),
|
||||
})
|
||||
remaining -= written
|
||||
}
|
||||
|
||||
return parts, nil
|
||||
}
|
||||
|
||||
func cleanupBackupFiles(paths ...string) error {
|
||||
var errs []error
|
||||
for _, path := range paths {
|
||||
if path == "" {
|
||||
continue
|
||||
}
|
||||
if err := os.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
errs = append(errs, fmt.Errorf("remove %s: %w", path, err))
|
||||
}
|
||||
}
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
Reference in New Issue
Block a user