Complete artifact binary transfer streaming

This commit is contained in:
npc0-hue
2026-09-03 13:27:22 +08:00
parent fe09d21a56
commit ec2462a312
8 changed files with 178 additions and 35 deletions
+72 -17
View File
@@ -3,6 +3,7 @@ package service
import (
"errors"
"fmt"
"io"
"net/url"
"sort"
"strings"
@@ -16,6 +17,11 @@ import (
const artifactDownloadStorageBehavior = "platform-durable-artifact-store"
const artifactPayloadCacheLimit = 4 * 1024 * 1024
type ArtifactContentStream struct {
Content domain.ArtifactContent
Body io.ReadCloser
}
func (svc *CoreService) GetArtifactForSession(sessionID string, artifactID string) (domain.Artifact, error) {
artifact, err := svc.store.Artifacts().Get(strings.TrimSpace(artifactID))
if err != nil {
@@ -74,22 +80,11 @@ func (svc *CoreService) ReadArtifactContentForSession(sessionID string, request
if artifact.State != domain.ArtifactStateAvailable {
return domain.ArtifactContent{}, validationError("artifact must be available before download")
}
limit := request.Limit
if limit == 0 {
if request.Offset == 0 {
limit = int(artifact.SizeBytes)
} else {
limit = validator.MaxArtifactDownloadBytes
}
offset, length, partial, err := artifactContentBounds(artifact, request)
if err != nil {
return domain.ArtifactContent{}, err
}
if request.Offset > artifact.SizeBytes {
return domain.ArtifactContent{}, validationError("artifact range exceeds metadata")
}
remaining := artifact.SizeBytes - request.Offset
if int64(limit) > remaining {
limit = int(remaining)
}
payload, err := svc.artifactStore.ReadPayloadRange(artifact.ID, request.Offset, limit)
payload, err := svc.artifactStore.ReadPayloadRange(artifact.ID, offset, int(length))
if err != nil {
return domain.ArtifactContent{}, err
}
@@ -99,12 +94,12 @@ func (svc *CoreService) ReadArtifactContentForSession(sessionID string, request
ArtifactID: artifact.ID,
Filename: filename,
ContentType: contentType,
Offset: request.Offset,
Offset: offset,
SizeBytes: int64(len(part)),
TotalSizeBytes: artifact.SizeBytes,
Checksum: artifact.Checksum,
ContentChecksum: validator.BytesChecksum(part),
Partial: request.Offset != 0 || int64(len(part)) != artifact.SizeBytes,
Partial: partial,
RangeSupported: true,
Payload: part,
StorageBehavior: artifactDownloadStorageBehavior,
@@ -116,6 +111,66 @@ func (svc *CoreService) ReadArtifactContentForSession(sessionID string, request
return domain.CopyArtifactContent(content), nil
}
func (svc *CoreService) OpenArtifactContentStreamForSession(sessionID string, request domain.ArtifactContentRequest) (ArtifactContentStream, error) {
request = domain.CopyArtifactContentRequest(request)
if err := validator.ValidateArtifactContentRequest(request); err != nil {
return ArtifactContentStream{}, err
}
artifact, err := svc.GetArtifactForSession(sessionID, request.ArtifactID)
if err != nil {
return ArtifactContentStream{}, err
}
if artifact.State != domain.ArtifactStateAvailable {
return ArtifactContentStream{}, validationError("artifact must be available before download")
}
offset, length, partial, err := artifactContentBounds(artifact, request)
if err != nil {
return ArtifactContentStream{}, err
}
contentChecksum := artifact.Checksum
if partial {
payload, err := svc.artifactStore.ReadPayloadRange(artifact.ID, offset, int(length))
if err != nil {
return ArtifactContentStream{}, err
}
contentChecksum = validator.BytesChecksum(payload)
}
reader, err := svc.artifactStore.OpenPayloadRange(artifact.ID, offset, length)
if err != nil {
return ArtifactContentStream{}, err
}
filename, contentType := svc.artifactDownloadPresentation(artifact)
content := domain.ArtifactContent{ArtifactID: artifact.ID, Filename: filename, ContentType: contentType, Offset: offset, SizeBytes: length, TotalSizeBytes: artifact.SizeBytes, Checksum: artifact.Checksum, ContentChecksum: contentChecksum, Partial: partial, RangeSupported: true, StorageBehavior: artifactDownloadStorageBehavior, ServedAt: svc.now()}
if err := validator.ValidateArtifactContent(content); err != nil {
_ = reader.Close()
return ArtifactContentStream{}, err
}
return ArtifactContentStream{Content: domain.CopyArtifactContent(content), Body: reader}, nil
}
func artifactContentBounds(artifact domain.Artifact, request domain.ArtifactContentRequest) (int64, int64, bool, error) {
if request.Offset > artifact.SizeBytes {
return 0, 0, false, validationError("artifact range exceeds metadata")
}
length := int64(request.Limit)
if request.Limit == 0 {
if request.Offset == 0 {
length = artifact.SizeBytes
} else {
length = int64(validator.MaxArtifactDownloadBytes)
}
}
remaining := artifact.SizeBytes - request.Offset
if length > remaining {
length = remaining
}
if length <= 0 {
return 0, 0, false, validationError("artifact range is empty")
}
partial := request.Offset != 0 || length != artifact.SizeBytes
return request.Offset, length, partial, nil
}
func (svc *CoreService) authorizeArtifactAccess(sessionID string, artifact domain.Artifact) error {
user, err := svc.GetCurrentUser(sessionID)
if err != nil {