85 lines
2.5 KiB
TypeScript
85 lines
2.5 KiB
TypeScript
import { Injectable, inject } from '@angular/core';
|
|
import { RealtimeSessionFacade } from '../../../../core/realtime';
|
|
import { AttachmentStorageService } from '../../infrastructure/services/attachment-storage.service';
|
|
import { FILE_CHUNK_SIZE_BYTES } from '../../domain/constants/attachment-transfer.constants';
|
|
import { FileChunkEvent } from '../../domain/models/attachment-transfer.model';
|
|
import {
|
|
arrayBufferToBase64,
|
|
decodeBase64,
|
|
iterateBlobChunks
|
|
} from '../../../../shared-kernel';
|
|
|
|
@Injectable({ providedIn: 'root' })
|
|
export class AttachmentTransferTransportService {
|
|
private readonly webrtc = inject(RealtimeSessionFacade);
|
|
private readonly attachmentStorage = inject(AttachmentStorageService);
|
|
|
|
decodeBase64(base64: string): Uint8Array {
|
|
return decodeBase64(base64);
|
|
}
|
|
|
|
async streamFileToPeer(
|
|
targetPeerId: string,
|
|
messageId: string,
|
|
fileId: string,
|
|
file: File,
|
|
isCancelled: () => boolean
|
|
): Promise<void> {
|
|
for await (const chunk of iterateBlobChunks(file, FILE_CHUNK_SIZE_BYTES)) {
|
|
if (isCancelled())
|
|
break;
|
|
|
|
const fileChunkEvent: FileChunkEvent = {
|
|
type: 'file-chunk',
|
|
messageId,
|
|
fileId,
|
|
index: chunk.index,
|
|
total: chunk.total,
|
|
data: chunk.base64
|
|
};
|
|
|
|
await this.webrtc.sendToPeerBuffered(targetPeerId, fileChunkEvent);
|
|
}
|
|
}
|
|
|
|
async streamFileFromDiskToPeer(
|
|
targetPeerId: string,
|
|
messageId: string,
|
|
fileId: string,
|
|
diskPath: string,
|
|
isCancelled: () => boolean
|
|
): Promise<void> {
|
|
const base64Full = await this.attachmentStorage.readFile(diskPath);
|
|
|
|
if (!base64Full)
|
|
return;
|
|
|
|
const fileBytes = decodeBase64(base64Full);
|
|
const totalChunks = Math.ceil(fileBytes.byteLength / FILE_CHUNK_SIZE_BYTES);
|
|
|
|
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
|
|
if (isCancelled())
|
|
break;
|
|
|
|
const start = chunkIndex * FILE_CHUNK_SIZE_BYTES;
|
|
const end = Math.min(fileBytes.byteLength, start + FILE_CHUNK_SIZE_BYTES);
|
|
const slice = fileBytes.subarray(start, end);
|
|
const sliceBuffer = (slice.buffer as ArrayBuffer).slice(
|
|
slice.byteOffset,
|
|
slice.byteOffset + slice.byteLength
|
|
);
|
|
const base64Chunk = arrayBufferToBase64(sliceBuffer);
|
|
const fileChunkEvent: FileChunkEvent = {
|
|
type: 'file-chunk',
|
|
messageId,
|
|
fileId,
|
|
index: chunkIndex,
|
|
total: totalChunks,
|
|
data: base64Chunk
|
|
};
|
|
|
|
this.webrtc.sendToPeer(targetPeerId, fileChunkEvent);
|
|
}
|
|
}
|
|
}
|