feat: 新增 /upload_flash_transfer 文件闪传上传#113
Open
kengwang wants to merge 1 commit into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds end-to-end support for “flash transfer” uploads, including a new Milky API endpoint and the corresponding Core protocol/services and upload context changes.
Changes:
- Introduces
upload_flash_transferAPI handler + JSON source-gen registration. - Adds Core flash transfer events, protobuf packet models, and OIDB services to create/register/upload/complete file sets.
- Extends
FlashTransferContextandOperationLogicto perform chunked uploads with file binding metadata.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| Lagrange.Milky/Utility/JsonUtility.cs | Registers flash transfer DTOs for System.Text.Json source generation. |
| Lagrange.Milky/Api/Handler/File/UploadFlashTransferHandler.cs | Adds the upload_flash_transfer API handler and request/response DTOs. |
| Lagrange.Core/Internal/Services/System/FlashTransferService.cs | Implements OIDB services for flash transfer file set lifecycle and upload steps. |
| Lagrange.Core/Internal/Services/System/FetchGroupMembersService.cs | Updates the OIDB service route string for fetching group members. |
| Lagrange.Core/Internal/Packets/Service/FlashTransferUploadReq.cs | Renames/provides additional upload request fields (e.g., binding) for chunk uploads. |
| Lagrange.Core/Internal/Packets/Service/FlashTransfer.cs | Adds protobuf models for flash transfer requests/responses. |
| Lagrange.Core/Internal/Logic/OperationLogic.cs | Adds UploadFlashTransfer workflow orchestration in core logic. |
| Lagrange.Core/Internal/Events/System/FlashTransferEvent.cs | Introduces internal events and file metadata helpers for flash transfer. |
| Lagrange.Core/Internal/Context/FlashTransferContext.cs | Extends chunk upload to support upload index, binding, and configurable upload URL. |
| Lagrange.Core/Common/Response/BotFlashTransferUpload.cs | Adds a Core response model for flash transfer upload results. |
| Lagrange.Core/Common/Interface/MessageExt.cs | Exposes UploadFlashTransfer as a BotContext extension method. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+36
to
+54
| public class UploadFlashTransferParameter(List<UploadFlashTransferFileParameter> files, string? title = null) | ||
| { | ||
| [JsonRequired] | ||
| [JsonPropertyName("files")] | ||
| public List<UploadFlashTransferFileParameter> Files { get; init; } = files; | ||
|
|
||
| [JsonPropertyName("fileset_name")] | ||
| public string? Title { get; init; } = title; | ||
| } | ||
|
|
||
| public class UploadFlashTransferFileParameter(string fileUri, string? fileName = null) | ||
| { | ||
| [JsonRequired] | ||
| [JsonPropertyName("file_uri")] | ||
| public string FileUri { get; init; } = fileUri; | ||
|
|
||
| [JsonPropertyName("file_name")] | ||
| public string? FileName { get; init; } = fileName; | ||
| } |
Comment on lines
+314
to
+324
| internal static string ResolveAsciiTitle(string title) | ||
| { | ||
| Span<char> chars = stackalloc char[title.Length]; | ||
| int length = 0; | ||
| foreach (char c in title) | ||
| { | ||
| chars[length++] = c < 128 && !char.IsWhiteSpace(c) ? c : '_'; | ||
| } | ||
|
|
||
| return new string(chars[..length]); | ||
| } |
Comment on lines
+268
to
+300
| uint uploadScene = 3; | ||
| foreach (var file in flashTransferFiles) | ||
| { | ||
| var authorize = await context.EventContext.SendEvent<FlashTransferUploadAuthorizeEventResp>(new FlashTransferUploadAuthorizeEventReq(createResp.FileSetId, file, uploadScene++)); | ||
|
|
||
| if (string.IsNullOrEmpty(authorize.UploadToken) && string.IsNullOrEmpty(authorize.ResourceKey)) | ||
| { | ||
| context.LogWarning(Tag, | ||
| "FlashTransfer authorize returned neither upload token nor resource key: fileName={0}, fileId={1}, index={2}, size={3}, host={4}, appId={5}, bindingStage={6}, bindingField5={7}, bindingField6={8}", | ||
| null, | ||
| file.FileName, | ||
| file.FileId, | ||
| file.Index, | ||
| file.Stream.Length, | ||
| authorize.UploadHost, | ||
| authorize.AppId, | ||
| authorize.BindingStage, | ||
| authorize.BindingField5, | ||
| authorize.BindingField6); | ||
| throw new OperationException(-1, $"FlashTransfer upload authorization did not return an upload token or resource key for file {file.FileName}"); | ||
| } | ||
|
|
||
| uint bindingStage = authorize.BindingStage == 0 ? file.Index : authorize.BindingStage; | ||
| if (!string.IsNullOrEmpty(authorize.UploadToken)) | ||
| { | ||
| bool success = await context.FlashTransferContext.UploadFile(authorize.UploadToken, authorize.UploadHost, authorize.AppId, file.Index, createResp.FileSetId, file.FileId, bindingStage, file.FileType, authorize.BindingField5, authorize.BindingField6, file.Stream); | ||
| if (!success) throw new OperationException(-1, "FlashTransfer file upload failed"); | ||
| } | ||
|
|
||
| await context.EventContext.SendEvent<FlashTransferUploadCompleteEventResp>(new FlashTransferUploadCompleteEventReq(createResp.FileSetId, file, authorize.ResourceKey, uploadScene++, bindingStage, authorize.BindingField5, authorize.BindingField6)); | ||
| } | ||
|
|
||
| await context.EventContext.SendEvent<FlashTransferUpdateFileSetStatusEventResp>(new FlashTransferUpdateFileSetStatusEventReq(createResp.FileSetId, 6)); |
| return await UploadFile(uploadToken, appId, uploadIndex, bodyStream, binding, $"https://{uploadHost}/sliceupload"); | ||
| } | ||
|
|
||
| private async Task<bool> UploadFile(string uKey, uint appId, uint uploadIndex, Stream bodyStream, FlashTransferUploadFileBinding? binding, string? url) |
| } | ||
|
|
||
| private async Task<bool> UploadChunk(string uKey, uint appId, uint start, FlashTransferSha1StateV chunkSha1S, byte[] body) | ||
| private async Task<bool> UploadChunk(string uKey, uint appId, uint uploadIndex, uint start, FlashTransferSha1StateV chunkSha1S, byte[] body, FlashTransferUploadFileBinding? binding, string? url) |
| }; | ||
| var payload = ProtoHelper.Serialize(req).ToArray(); | ||
| var request = new HttpRequestMessage(HttpMethod.Post, _url) | ||
| var request = new HttpRequestMessage(HttpMethod.Post, url) |
XeronOwO
approved these changes
Jun 5, 2026
KusaGumina
previously approved these changes
Jun 5, 2026
`/upload_flash_transfer`
b9c745c to
b9df955
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
新增文件闪传上传