Makes TransferId uint32

This commit is contained in:
Rune Harlyk
2026-01-22 19:58:50 +01:00
committed by Rune Harlyk
parent aff50d6a9c
commit 4ac54279a8
6 changed files with 59 additions and 99 deletions
+4 -4
View File
@@ -1,8 +1,8 @@
declare module 'app-env' { declare module "app-env" {
interface ENV { interface ENV {
VITE_USE_HOST_NAME: boolean VITE_USE_HOST_NAME: boolean;
} }
const appEnv: ENV const appEnv: ENV;
export default appEnv export default appEnv;
} }
+6 -5
View File
@@ -33,7 +33,7 @@ export interface ListResult {
} }
export interface TransferProgress { export interface TransferProgress {
transferId: string transferId: number
bytesTransferred: number bytesTransferred: number
totalBytes: number totalBytes: number
chunksCompleted: number chunksCompleted: number
@@ -59,7 +59,7 @@ interface ActiveDownload {
interface ActiveUpload { interface ActiveUpload {
path: string path: string
transferId: string transferId: number
totalChunks: number totalChunks: number
chunksSent: number chunksSent: number
resolve: (result: { success: boolean; error?: string }) => void resolve: (result: { success: boolean; error?: string }) => void
@@ -69,8 +69,8 @@ interface ActiveUpload {
} }
export class FileSystemClient { export class FileSystemClient {
private activeDownloads = new Map<string, ActiveDownload>() private activeDownloads = new Map<number, ActiveDownload>()
private activeUploads = new Map<string, ActiveUpload>() private activeUploads = new Map<number, ActiveUpload>()
private pendingDownloads = new Map< private pendingDownloads = new Map<
string, string,
{ {
@@ -433,7 +433,7 @@ export class FileSystemClient {
/** /**
* Cancel an ongoing transfer * Cancel an ongoing transfer
*/ */
async cancelTransfer(transferId: string): Promise<{ success: boolean }> { async cancelTransfer(transferId: number): Promise<{ success: boolean }> {
const request: FSCancelTransfer = { transferId } const request: FSCancelTransfer = { transferId }
// Clean up local state // Clean up local state
@@ -507,6 +507,7 @@ export class FileSystemClient {
* Cleanup listeners when no longer needed * Cleanup listeners when no longer needed
*/ */
destroy() { destroy() {
this.metadataListenerCleanup?.()
this.downloadListenerCleanup?.() this.downloadListenerCleanup?.()
this.completeListenerCleanup?.() this.completeListenerCleanup?.()
this.uploadCompleteListenerCleanup?.() this.uploadCompleteListenerCleanup?.()
+6 -7
View File
@@ -82,24 +82,23 @@ class FileSystemHandler {
void processPendingDownloads(); void processPendingDownloads();
private: private:
std::map<std::string, DownloadState> downloads_; std::map<uint32_t, DownloadState> downloads_;
std::map<std::string, UploadState> uploads_; std::map<uint32_t, UploadState> uploads_;
uint32_t transferIdCounter_; uint32_t transferIdCounter_;
inline uint32_t generateTransferId() { return ++transferIdCounter_; }
SendMetadataCallback sendMetadataCallback_; SendMetadataCallback sendMetadataCallback_;
SendCallback sendDataCallback_; SendCallback sendDataCallback_;
SendCompleteCallback sendCompleteCallback_; SendCompleteCallback sendCompleteCallback_;
SendUploadCompleteCallback sendUploadCompleteCallback_; SendUploadCompleteCallback sendUploadCompleteCallback_;
std::string generateTransferId();
void listDirectory(const std::string& path, socket_message_FSListResponse& response); void listDirectory(const std::string& path, socket_message_FSListResponse& response);
bool deleteRecursive(const std::string& path); bool deleteRecursive(const std::string& path);
// Send next chunk for a download bool sendNextDownloadChunk(uint32_t transferId);
bool sendNextDownloadChunk(const std::string& transferId);
// Finalize upload and send completion message void finalizeUpload(uint32_t transferId, bool success, const std::string& error = "");
void finalizeUpload(const std::string& transferId, bool success, const std::string& error = "");
}; };
extern FileSystemHandler fsHandler; extern FileSystemHandler fsHandler;
+29 -59
View File
@@ -24,27 +24,20 @@ void FileSystemHandler::setSendCallbacks(
sendUploadCompleteCallback_ = sendUploadComplete; sendUploadCompleteCallback_ = sendUploadComplete;
} }
// This transfer is can be simplified, current "xfer" is just to distinguish a fs transfer id from any other, but relistically is just a waste of 5 bytes for every chunk sent
std::string FileSystemHandler::generateTransferId() {
return "xfer_" + std::to_string(millis()) + "_" + std::to_string(++transferIdCounter_);
}
void FileSystemHandler::cleanupExpiredTransfers() { void FileSystemHandler::cleanupExpiredTransfers() {
uint32_t now = millis(); uint32_t now = millis();
// Cleanup expired downloads
auto dlIt = downloads_.begin(); auto dlIt = downloads_.begin();
while (dlIt != downloads_.end()) { while (dlIt != downloads_.end()) {
if (now - dlIt->second.lastActivityTime > FS_TRANSFER_TIMEOUT) { if (now - dlIt->second.lastActivityTime > FS_TRANSFER_TIMEOUT) {
if (dlIt->second.file) { if (dlIt->second.file) {
dlIt->second.file.close(); dlIt->second.file.close();
} }
ESP_LOGW(TAG, "Download %s timed out", dlIt->first.c_str()); ESP_LOGW(TAG, "Download %u timed out", dlIt->first);
// Send error completion
if (sendCompleteCallback_) { if (sendCompleteCallback_) {
socket_message_FSDownloadComplete complete = socket_message_FSDownloadComplete_init_zero; socket_message_FSDownloadComplete complete = socket_message_FSDownloadComplete_init_zero;
strncpy(complete.transfer_id, dlIt->first.c_str(), sizeof(complete.transfer_id) - 1); complete.transfer_id = dlIt->first;
complete.success = false; complete.success = false;
strncpy(complete.error, "Transfer timed out", sizeof(complete.error) - 1); strncpy(complete.error, "Transfer timed out", sizeof(complete.error) - 1);
complete.total_chunks = dlIt->second.chunksSent; complete.total_chunks = dlIt->second.chunksSent;
@@ -58,21 +51,18 @@ void FileSystemHandler::cleanupExpiredTransfers() {
} }
} }
// Cleanup expired uploads
auto ulIt = uploads_.begin(); auto ulIt = uploads_.begin();
while (ulIt != uploads_.end()) { while (ulIt != uploads_.end()) {
if (now - ulIt->second.lastActivityTime > FS_TRANSFER_TIMEOUT) { if (now - ulIt->second.lastActivityTime > FS_TRANSFER_TIMEOUT) {
if (ulIt->second.file) { if (ulIt->second.file) {
ulIt->second.file.close(); ulIt->second.file.close();
} }
// Delete partial file
ESP_FS.remove(ulIt->second.path.c_str()); ESP_FS.remove(ulIt->second.path.c_str());
ESP_LOGW(TAG, "Upload %s timed out, deleted partial file", ulIt->first.c_str()); ESP_LOGW(TAG, "Upload %u timed out, deleted partial file", ulIt->first);
// Send error completion
if (sendUploadCompleteCallback_) { if (sendUploadCompleteCallback_) {
socket_message_FSUploadComplete complete = socket_message_FSUploadComplete_init_zero; socket_message_FSUploadComplete complete = socket_message_FSUploadComplete_init_zero;
strncpy(complete.transfer_id, ulIt->first.c_str(), sizeof(complete.transfer_id) - 1); complete.transfer_id = ulIt->first;
complete.success = false; complete.success = false;
strncpy(complete.error, "Transfer timed out", sizeof(complete.error) - 1); strncpy(complete.error, "Transfer timed out", sizeof(complete.error) - 1);
complete.chunks_received = ulIt->second.chunksReceived; complete.chunks_received = ulIt->second.chunksReceived;
@@ -238,14 +228,13 @@ void FileSystemHandler::handleDownloadRequest(const socket_message_FSDownloadReq
uint32_t fileSize = file.size(); uint32_t fileSize = file.size();
uint32_t chunkSize = FS_MAX_CHUNK_SIZE; uint32_t chunkSize = FS_MAX_CHUNK_SIZE;
uint32_t totalChunks = (fileSize + chunkSize - 1) / chunkSize; uint32_t totalChunks = (fileSize + chunkSize - 1) / chunkSize;
if (totalChunks == 0) totalChunks = 1; // Handle empty files if (totalChunks == 0) totalChunks = 1;
std::string transferId = generateTransferId(); uint32_t transferId = generateTransferId();
// Send metadata first so client knows exact file size and can allocate buffer
if (sendMetadataCallback_) { if (sendMetadataCallback_) {
socket_message_FSDownloadMetadata metadata = socket_message_FSDownloadMetadata_init_zero; socket_message_FSDownloadMetadata metadata = socket_message_FSDownloadMetadata_init_zero;
strncpy(metadata.transfer_id, transferId.c_str(), sizeof(metadata.transfer_id) - 1); metadata.transfer_id = transferId;
metadata.success = true; metadata.success = true;
metadata.file_size = fileSize; metadata.file_size = fileSize;
metadata.total_chunks = totalChunks; metadata.total_chunks = totalChunks;
@@ -264,8 +253,8 @@ void FileSystemHandler::handleDownloadRequest(const socket_message_FSDownloadReq
downloads_[transferId] = state; downloads_[transferId] = state;
ESP_LOGI(TAG, "Download started: %s, size=%u, chunks=%u, id=%s", ESP_LOGI(TAG, "Download started: %s, size=%u, chunks=%u, id=%u",
path.c_str(), fileSize, totalChunks, transferId.c_str()); path.c_str(), fileSize, totalChunks, transferId);
// Start streaming chunks immediately // Start streaming chunks immediately
while (sendNextDownloadChunk(transferId)) { while (sendNextDownloadChunk(transferId)) {
@@ -274,7 +263,7 @@ void FileSystemHandler::handleDownloadRequest(const socket_message_FSDownloadReq
} }
} }
bool FileSystemHandler::sendNextDownloadChunk(const std::string& transferId) { bool FileSystemHandler::sendNextDownloadChunk(uint32_t transferId) {
auto it = downloads_.find(transferId); auto it = downloads_.find(transferId);
if (it == downloads_.end()) { if (it == downloads_.end()) {
return false; return false;
@@ -283,12 +272,10 @@ bool FileSystemHandler::sendNextDownloadChunk(const std::string& transferId) {
DownloadState& state = it->second; DownloadState& state = it->second;
state.lastActivityTime = millis(); state.lastActivityTime = millis();
// Check if we're done
if (state.chunksSent >= state.totalChunks) { if (state.chunksSent >= state.totalChunks) {
// Send completion message
if (sendCompleteCallback_) { if (sendCompleteCallback_) {
socket_message_FSDownloadComplete complete = socket_message_FSDownloadComplete_init_zero; socket_message_FSDownloadComplete complete = socket_message_FSDownloadComplete_init_zero;
strncpy(complete.transfer_id, transferId.c_str(), sizeof(complete.transfer_id) - 1); complete.transfer_id = transferId;
complete.success = true; complete.success = true;
complete.total_chunks = state.totalChunks; complete.total_chunks = state.totalChunks;
complete.file_size = state.fileSize; complete.file_size = state.fileSize;
@@ -297,31 +284,27 @@ bool FileSystemHandler::sendNextDownloadChunk(const std::string& transferId) {
state.file.close(); state.file.close();
downloads_.erase(it); downloads_.erase(it);
ESP_LOGI(TAG, "Download completed: %s", transferId.c_str()); ESP_LOGI(TAG, "Download completed: %u", transferId);
return false; return false;
} }
// Allocate data struct on heap to avoid stack overflow (it contains 16KB buffer)
auto data = new socket_message_FSDownloadData(); auto data = new socket_message_FSDownloadData();
memset(data, 0, sizeof(socket_message_FSDownloadData)); memset(data, 0, sizeof(socket_message_FSDownloadData));
strncpy(data->transfer_id, transferId.c_str(), sizeof(data->transfer_id) - 1); data->transfer_id = transferId;
data->chunk_index = state.chunksSent; data->chunk_index = state.chunksSent;
// Calculate chunk size (last chunk might be smaller)
uint32_t bytesToRead = state.chunkSize; uint32_t bytesToRead = state.chunkSize;
uint32_t position = state.chunksSent * state.chunkSize; uint32_t position = state.chunksSent * state.chunkSize;
if (position + bytesToRead > state.fileSize) { if (position + bytesToRead > state.fileSize) {
bytesToRead = state.fileSize - position; bytesToRead = state.fileSize - position;
} }
// Read chunk data
size_t bytesRead = state.file.read(data->data.bytes, bytesToRead); size_t bytesRead = state.file.read(data->data.bytes, bytesToRead);
if (bytesRead == 0 && bytesToRead > 0) { if (bytesRead == 0 && bytesToRead > 0) {
// Read error - send error completion
delete data; delete data;
if (sendCompleteCallback_) { if (sendCompleteCallback_) {
socket_message_FSDownloadComplete complete = socket_message_FSDownloadComplete_init_zero; socket_message_FSDownloadComplete complete = socket_message_FSDownloadComplete_init_zero;
strncpy(complete.transfer_id, transferId.c_str(), sizeof(complete.transfer_id) - 1); complete.transfer_id = transferId;
complete.success = false; complete.success = false;
strncpy(complete.error, "Failed to read file", sizeof(complete.error) - 1); strncpy(complete.error, "Failed to read file", sizeof(complete.error) - 1);
complete.total_chunks = state.chunksSent; complete.total_chunks = state.chunksSent;
@@ -331,12 +314,11 @@ bool FileSystemHandler::sendNextDownloadChunk(const std::string& transferId) {
state.file.close(); state.file.close();
downloads_.erase(it); downloads_.erase(it);
ESP_LOGE(TAG, "Download failed - read error: %s", transferId.c_str()); ESP_LOGE(TAG, "Download failed - read error: %u", transferId);
return false; return false;
} }
data->data.size = bytesRead; data->data.size = bytesRead;
// Send chunk
if (sendDataCallback_) { if (sendDataCallback_) {
sendDataCallback_(*data, state.clientId); sendDataCallback_(*data, state.clientId);
} }
@@ -348,12 +330,10 @@ bool FileSystemHandler::sendNextDownloadChunk(const std::string& transferId) {
return true; return true;
} }
// ===== STREAMING UPLOAD ===== // ===== STREAMING UPLOAD =====
socket_message_FSUploadStartResponse FileSystemHandler::handleUploadStart( socket_message_FSUploadStartResponse FileSystemHandler::handleUploadStart(const socket_message_FSUploadStart& req,
const socket_message_FSUploadStart& req, int clientId int clientId) {
) {
socket_message_FSUploadStartResponse response = socket_message_FSUploadStartResponse_init_zero; socket_message_FSUploadStartResponse response = socket_message_FSUploadStartResponse_init_zero;
std::string path(req.path); std::string path(req.path);
@@ -387,11 +367,9 @@ socket_message_FSUploadStartResponse FileSystemHandler::handleUploadStart(
return response; return response;
} }
// Set file buffer size large, so we use ram to write to - and only flush when we need it (TODO: currently it is periodical)
// Set buffer size so 1 mb (static) TODO: Check that there is enough ram to do this
file.setBufferSize(1000000); file.setBufferSize(1000000);
std::string transferId = generateTransferId(); uint32_t transferId = generateTransferId();
UploadState state; UploadState state;
state.path = path; state.path = path;
@@ -407,19 +385,19 @@ socket_message_FSUploadStartResponse FileSystemHandler::handleUploadStart(
uploads_[transferId] = state; uploads_[transferId] = state;
response.success = true; response.success = true;
strncpy(response.transfer_id, transferId.c_str(), sizeof(response.transfer_id) - 1); response.transfer_id = transferId;
ESP_LOGI(TAG, "Upload started: %s, id=%s", path.c_str(), transferId.c_str()); ESP_LOGI(TAG, "Upload started: %s, id=%u", path.c_str(), transferId);
return response; return response;
} }
void FileSystemHandler::handleUploadData(const socket_message_FSUploadData& req) { void FileSystemHandler::handleUploadData(const socket_message_FSUploadData& req) {
std::string transferId(req.transfer_id); uint32_t transferId = req.transfer_id;
auto it = uploads_.find(transferId); auto it = uploads_.find(transferId);
if (it == uploads_.end()) { if (it == uploads_.end()) {
ESP_LOGW(TAG, "Upload data for unknown transfer: %s", transferId.c_str()); ESP_LOGW(TAG, "Upload data for unknown transfer: %u", transferId);
return; return;
} }
@@ -464,8 +442,7 @@ void FileSystemHandler::handleUploadData(const socket_message_FSUploadData& req)
} }
} }
// Note that the finalize upload takes an insane amount of time, as it is flushing from ram to an incredibly slow LittleFS SPIFFS (Tested to roughly 12 seconds for 1 mb file) void FileSystemHandler::finalizeUpload(uint32_t transferId, bool success, const std::string& error) {
void FileSystemHandler::finalizeUpload(const std::string& transferId, bool success, const std::string& error) {
auto it = uploads_.find(transferId); auto it = uploads_.find(transferId);
if (it == uploads_.end()) { if (it == uploads_.end()) {
return; return;
@@ -473,12 +450,10 @@ void FileSystemHandler::finalizeUpload(const std::string& transferId, bool succe
UploadState& state = it->second; UploadState& state = it->second;
// Close file
if (state.file) { if (state.file) {
state.file.close(); state.file.close();
} }
// Delete file on error
if (!success) { if (!success) {
ESP_FS.remove(state.path.c_str()); ESP_FS.remove(state.path.c_str());
ESP_LOGW(TAG, "Upload failed, deleted partial file: %s", state.path.c_str()); ESP_LOGW(TAG, "Upload failed, deleted partial file: %s", state.path.c_str());
@@ -486,10 +461,9 @@ void FileSystemHandler::finalizeUpload(const std::string& transferId, bool succe
ESP_LOGI(TAG, "Upload completed: %s (%u bytes)", state.path.c_str(), state.bytesReceived); ESP_LOGI(TAG, "Upload completed: %s (%u bytes)", state.path.c_str(), state.bytesReceived);
} }
// Send completion message
if (sendUploadCompleteCallback_) { if (sendUploadCompleteCallback_) {
socket_message_FSUploadComplete complete = socket_message_FSUploadComplete_init_zero; socket_message_FSUploadComplete complete = socket_message_FSUploadComplete_init_zero;
strncpy(complete.transfer_id, transferId.c_str(), sizeof(complete.transfer_id) - 1); complete.transfer_id = transferId;
complete.success = success; complete.success = success;
if (!error.empty()) { if (!error.empty()) {
strncpy(complete.error, error.c_str(), sizeof(complete.error) - 1); strncpy(complete.error, error.c_str(), sizeof(complete.error) - 1);
@@ -504,13 +478,11 @@ void FileSystemHandler::finalizeUpload(const std::string& transferId, bool succe
// ===== TRANSFER CONTROL ===== // ===== TRANSFER CONTROL =====
socket_message_FSCancelTransferResponse FileSystemHandler::handleCancelTransfer( socket_message_FSCancelTransferResponse FileSystemHandler::handleCancelTransfer(
const socket_message_FSCancelTransfer& req const socket_message_FSCancelTransfer& req) {
) {
socket_message_FSCancelTransferResponse response = socket_message_FSCancelTransferResponse_init_zero; socket_message_FSCancelTransferResponse response = socket_message_FSCancelTransferResponse_init_zero;
std::string transferId(req.transfer_id); uint32_t transferId = req.transfer_id;
strncpy(response.transfer_id, transferId.c_str(), sizeof(response.transfer_id) - 1); response.transfer_id = transferId;
// Check downloads
auto dlIt = downloads_.find(transferId); auto dlIt = downloads_.find(transferId);
if (dlIt != downloads_.end()) { if (dlIt != downloads_.end()) {
if (dlIt->second.file) { if (dlIt->second.file) {
@@ -518,21 +490,19 @@ socket_message_FSCancelTransferResponse FileSystemHandler::handleCancelTransfer(
} }
downloads_.erase(dlIt); downloads_.erase(dlIt);
response.success = true; response.success = true;
ESP_LOGI(TAG, "Download cancelled: %s", transferId.c_str()); ESP_LOGI(TAG, "Download cancelled: %u", transferId);
return response; return response;
} }
// Check uploads
auto ulIt = uploads_.find(transferId); auto ulIt = uploads_.find(transferId);
if (ulIt != uploads_.end()) { if (ulIt != uploads_.end()) {
if (ulIt->second.file) { if (ulIt->second.file) {
ulIt->second.file.close(); ulIt->second.file.close();
} }
// Delete partial upload file
ESP_FS.remove(ulIt->second.path.c_str()); ESP_FS.remove(ulIt->second.path.c_str());
uploads_.erase(ulIt); uploads_.erase(ulIt);
response.success = true; response.success = true;
ESP_LOGI(TAG, "Upload cancelled: %s", transferId.c_str()); ESP_LOGI(TAG, "Upload cancelled: %u", transferId);
return response; return response;
} }
-10
View File
@@ -15,22 +15,12 @@ socket_message.FSListResponse.directories max_count:20
# Streaming download messages # Streaming download messages
socket_message.FSDownloadRequest.path max_size:256 socket_message.FSDownloadRequest.path max_size:256
socket_message.FSDownloadMetadata.transfer_id max_size:64
socket_message.FSDownloadMetadata.error max_size:128 socket_message.FSDownloadMetadata.error max_size:128
socket_message.FSDownloadData.transfer_id max_size:64
socket_message.FSDownloadData.data max_size:16384 socket_message.FSDownloadData.data max_size:16384
socket_message.FSDownloadComplete.transfer_id max_size:64
socket_message.FSDownloadComplete.error max_size:128 socket_message.FSDownloadComplete.error max_size:128
# Streaming upload messages # Streaming upload messages
socket_message.FSUploadStart.path max_size:256 socket_message.FSUploadStart.path max_size:256
socket_message.FSUploadStartResponse.error max_size:128 socket_message.FSUploadStartResponse.error max_size:128
socket_message.FSUploadStartResponse.transfer_id max_size:64
socket_message.FSUploadData.transfer_id max_size:64
socket_message.FSUploadData.data max_size:16384 socket_message.FSUploadData.data max_size:16384
socket_message.FSUploadComplete.transfer_id max_size:64
socket_message.FSUploadComplete.error max_size:128 socket_message.FSUploadComplete.error max_size:128
# Transfer control
socket_message.FSCancelTransfer.transfer_id max_size:64
socket_message.FSCancelTransferResponse.transfer_id max_size:64
+8 -8
View File
@@ -54,7 +54,7 @@ message FSDownloadRequest {
} }
message FSDownloadMetadata { message FSDownloadMetadata {
string transfer_id = 1; // Transfer identifier uint32 transfer_id = 1; // Transfer identifier
bool success = 2; // True if file exists and is readable bool success = 2; // True if file exists and is readable
string error = 3; // Error message if failed string error = 3; // Error message if failed
uint32 file_size = 4; // Total file size in bytes uint32 file_size = 4; // Total file size in bytes
@@ -62,13 +62,13 @@ message FSDownloadMetadata {
} }
message FSDownloadData { message FSDownloadData {
string transfer_id = 1; // Transfer identifier uint32 transfer_id = 1; // Transfer identifier
uint32 chunk_index = 2; // Which chunk this is (0-based) uint32 chunk_index = 2; // Which chunk this is (0-based)
bytes data = 3; // Chunk data (up to 16KB) bytes data = 3; // Chunk data (up to 16KB)
} }
message FSDownloadComplete { message FSDownloadComplete {
string transfer_id = 1; uint32 transfer_id = 1;
bool success = 2; bool success = 2;
string error = 3; // Error message if failed string error = 3; // Error message if failed
uint32 total_chunks = 4; // Total chunks that were sent uint32 total_chunks = 4; // Total chunks that were sent
@@ -87,17 +87,17 @@ message FSUploadStart {
message FSUploadStartResponse { message FSUploadStartResponse {
bool success = 1; bool success = 1;
string error = 2; string error = 2;
string transfer_id = 3; // Unique ID for this transfer uint32 transfer_id = 3; // Unique ID for this transfer
} }
message FSUploadData { message FSUploadData {
string transfer_id = 1; // Transfer identifier uint32 transfer_id = 1; // Transfer identifier
uint32 chunk_index = 2; // Which chunk this is (0-based) uint32 chunk_index = 2; // Which chunk this is (0-based)
bytes data = 3; // Chunk data (up to 16KB) bytes data = 3; // Chunk data (up to 16KB)
} }
message FSUploadComplete { message FSUploadComplete {
string transfer_id = 1; uint32 transfer_id = 1;
bool success = 2; bool success = 2;
string error = 3; // Error message if failed string error = 3; // Error message if failed
uint32 chunks_received = 4; // Number of chunks actually received uint32 chunks_received = 4; // Number of chunks actually received
@@ -106,10 +106,10 @@ message FSUploadComplete {
// ===== TRANSFER CONTROL ===== // ===== TRANSFER CONTROL =====
message FSCancelTransfer { message FSCancelTransfer {
string transfer_id = 1; uint32 transfer_id = 1;
} }
message FSCancelTransferResponse { message FSCancelTransferResponse {
string transfer_id = 1; uint32 transfer_id = 1;
bool success = 2; bool success = 2;
} }