180 lines
5.4 KiB
TypeScript
180 lines
5.4 KiB
TypeScript
import * as native from "tc-native/connection";
|
|
import * as path from "path";
|
|
import {DownloadKey, DownloadTransfer, UploadKey, UploadTransfer} from "tc-shared/FileManager";
|
|
import {base64_encode_ab, str2ab8} from "tc-shared/utils/buffers";
|
|
|
|
class NativeFileDownload implements DownloadTransfer {
|
|
readonly key: DownloadKey;
|
|
private _handle: native.ft.NativeFileTransfer;
|
|
private _buffer: Uint8Array;
|
|
|
|
private _result: Promise<void>;
|
|
private _response: Response;
|
|
|
|
private _result_success: () => any;
|
|
private _result_error: (error: any) => any;
|
|
|
|
constructor(key: DownloadKey) {
|
|
this.key = key;
|
|
this._buffer = new Uint8Array(key.total_size);
|
|
this._handle = native.ft.spawn_connection({
|
|
client_transfer_id: key.client_transfer_id,
|
|
server_transfer_id: key.server_transfer_id,
|
|
|
|
remote_address: key.peer.hosts[0],
|
|
remote_port: key.peer.port,
|
|
|
|
transfer_key: key.key,
|
|
|
|
object: native.ft.download_transfer_object_from_buffer(this._buffer.buffer)
|
|
});
|
|
}
|
|
|
|
get_key(): DownloadKey {
|
|
return this.key;
|
|
}
|
|
|
|
async request_file(): Promise<Response> {
|
|
if(this._response)
|
|
return this._response;
|
|
|
|
try {
|
|
await (this._result || this._start_transfer());
|
|
} catch(error) {
|
|
throw error;
|
|
}
|
|
|
|
if(this._response)
|
|
return this._response;
|
|
|
|
const buffer = this._buffer.buffer.slice(this._buffer.byteOffset, this._buffer.byteOffset + Math.min(64, this._buffer.byteLength));
|
|
|
|
/* may another task has been stepped by and already set the response */
|
|
return this._response || (this._response = new Response(this._buffer, {
|
|
status: 200,
|
|
statusText: "success",
|
|
headers: {
|
|
"X-media-bytes": base64_encode_ab(buffer)
|
|
|
|
}
|
|
}));
|
|
}
|
|
|
|
_start_transfer() : Promise<void> {
|
|
return this._result = new Promise((resolve, reject) => {
|
|
this._result_error = (error) => {
|
|
this._result_error = undefined;
|
|
this._result_success = undefined;
|
|
reject(error);
|
|
};
|
|
this._result_success = () => {
|
|
this._result_error = undefined;
|
|
this._result_success = undefined;
|
|
resolve();
|
|
};
|
|
|
|
this._handle.callback_failed = this._result_error;
|
|
this._handle.callback_finished = aborted => {
|
|
if(aborted)
|
|
this._result_error("aborted");
|
|
else
|
|
this._result_success();
|
|
};
|
|
|
|
this._handle.start();
|
|
});
|
|
}
|
|
}
|
|
|
|
class NativeFileUpload implements UploadTransfer {
|
|
readonly transfer_key: UploadKey;
|
|
private _handle: native.ft.NativeFileTransfer;
|
|
|
|
private _result: Promise<void>;
|
|
|
|
private _result_success: () => any;
|
|
private _result_error: (error: any) => any;
|
|
|
|
constructor(key: UploadKey) {
|
|
this.transfer_key = key;
|
|
}
|
|
|
|
async put_data(data: BlobPart | File) : Promise<void> {
|
|
if(this._result) {
|
|
await this._result;
|
|
return;
|
|
}
|
|
|
|
let buffer: native.ft.FileTransferSource;
|
|
|
|
if(data instanceof File) {
|
|
if(data.size != this.transfer_key.total_size)
|
|
throw "invalid size";
|
|
|
|
buffer = native.ft.upload_transfer_object_from_file(path.dirname(data.path), data.name);
|
|
} else if(typeof(data) === "string") {
|
|
if(data.length != this.transfer_key.total_size)
|
|
throw "invalid size";
|
|
|
|
buffer = native.ft.upload_transfer_object_from_buffer(str2ab8(data));
|
|
} else {
|
|
let buf = <BufferSource>data;
|
|
if(buf.byteLength != this.transfer_key.total_size)
|
|
throw "invalid size";
|
|
|
|
if(ArrayBuffer.isView(buf))
|
|
buf = buf.buffer.slice(buf.byteOffset);
|
|
|
|
buffer = native.ft.upload_transfer_object_from_buffer(buf);
|
|
}
|
|
|
|
this._handle = native.ft.spawn_connection({
|
|
client_transfer_id: this.transfer_key.client_transfer_id,
|
|
server_transfer_id: this.transfer_key.server_transfer_id,
|
|
|
|
remote_address: this.transfer_key.peer.hosts[0],
|
|
remote_port: this.transfer_key.peer.port,
|
|
|
|
transfer_key: this.transfer_key.key,
|
|
|
|
object: buffer
|
|
});
|
|
|
|
await (this._result = new Promise((resolve, reject) => {
|
|
this._result_error = (error) => {
|
|
this._result_error = undefined;
|
|
this._result_success = undefined;
|
|
reject(error);
|
|
};
|
|
this._result_success = () => {
|
|
this._result_error = undefined;
|
|
this._result_success = undefined;
|
|
resolve();
|
|
};
|
|
|
|
this._handle.callback_failed = this._result_error;
|
|
this._handle.callback_finished = aborted => {
|
|
if(aborted)
|
|
this._result_error("aborted");
|
|
else
|
|
this._result_success();
|
|
};
|
|
|
|
this._handle.start();
|
|
}));
|
|
}
|
|
|
|
get_key(): UploadKey {
|
|
return this.transfer_key;
|
|
}
|
|
}
|
|
|
|
|
|
export function spawn_download_transfer(key: DownloadKey) : DownloadTransfer {
|
|
return new NativeFileDownload(key);
|
|
}
|
|
|
|
|
|
export function spawn_upload_transfer(key: UploadKey) : UploadTransfer {
|
|
return new NativeFileUpload(key);
|
|
} |