feat(remote): 完善文件传输功能及WebSocket支持

This commit is contained in:
2026-03-10 01:41:02 +08:00
parent 6d5520dfa5
commit 84e455d9a6
19 changed files with 1263 additions and 5 deletions

View File

@@ -27,4 +27,11 @@ localIP = "127.0.0.1"
localPort = 3002
remotePort = 8082
[[proxies]]
name = "filetransfer-remote"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3003
remotePort = 8083

View File

@@ -68,6 +68,17 @@ router.get('/browse', (req, res) => {
try {
const filePath = req.query.path || '';
const allowSystem = req.query.allowSystem === 'true';
if (allowSystem && !filePath) {
const drives = fileService.getDrives();
res.json({
items: drives,
currentPath: '',
parentPath: null
});
return;
}
const result = fileService.browseDirectory(filePath, allowSystem);
res.json(result);
} catch (error) {

View File

@@ -0,0 +1,261 @@
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const logger = require('../utils/logger');
const MessageTypes = require('./messageTypes');
const { fileService } = require('../services/file');
const CHUNK_SIZE = 5 * 1024 * 1024;
class FileHandler {
constructor() {
this.uploadSessions = new Map();
}
handleMessage(message, ws) {
const { type, requestId, ...data } = message;
switch (type) {
case MessageTypes.FILE_LIST_GET:
this.handleFileList(ws, requestId);
break;
case MessageTypes.FILE_BROWSE:
this.handleFileBrowse(ws, data, requestId);
break;
case MessageTypes.FILE_UPLOAD_START:
this.handleFileUploadStart(ws, data, requestId);
break;
case MessageTypes.FILE_UPLOAD_CHUNK:
this.handleFileUploadChunk(ws, data, requestId);
break;
case MessageTypes.FILE_UPLOAD_MERGE:
this.handleFileUploadMerge(ws, data, requestId);
break;
case MessageTypes.FILE_DOWNLOAD_START:
this.handleFileDownload(ws, data, requestId);
break;
case MessageTypes.FILE_DELETE:
this.handleFileDelete(ws, data, requestId);
break;
default:
logger.debug('Unknown file message type', { type });
}
}
sendResponse(ws, type, requestId, payload) {
ws.send(JSON.stringify({
type,
requestId,
...payload
}));
}
handleFileList(ws, requestId) {
try {
const files = fileService.getFileList();
this.sendResponse(ws, MessageTypes.FILE_LIST, requestId, { files });
} catch (error) {
logger.error('Failed to get file list', { error: error.message });
this.sendResponse(ws, MessageTypes.FILE_LIST, requestId, { files: [], error: error.message });
}
}
handleFileBrowse(ws, data, requestId) {
try {
const { path: dirPath, allowSystem } = data;
if (allowSystem && !dirPath) {
const drives = fileService.getDrives();
this.sendResponse(ws, MessageTypes.FILE_BROWSE_RESULT, requestId, {
items: drives,
currentPath: '',
parentPath: null
});
return;
}
const result = fileService.browseDirectory(dirPath || '', allowSystem === true);
this.sendResponse(ws, MessageTypes.FILE_BROWSE_RESULT, requestId, result);
} catch (error) {
logger.error('Failed to browse directory', { error: error.message });
this.sendResponse(ws, MessageTypes.FILE_BROWSE_RESULT, requestId, { items: [], error: error.message });
}
}
handleFileUploadStart(ws, data, requestId) {
try {
const { filename, totalChunks, fileSize } = data;
const fileId = data.fileId || requestId || crypto.randomBytes(16).toString('hex');
this.uploadSessions.set(fileId, {
filename,
totalChunks,
fileSize,
chunks: new Map(),
createdAt: Date.now()
});
this.sendResponse(ws, MessageTypes.FILE_UPLOAD_START, requestId, {
fileId,
chunkSize: CHUNK_SIZE,
message: 'Upload session started'
});
} catch (error) {
logger.error('Failed to start upload', { error: error.message });
this.sendResponse(ws, MessageTypes.FILE_UPLOAD_RESULT, requestId, { success: false, error: error.message });
}
}
handleFileUploadChunk(ws, data, requestId) {
try {
const { fileId, chunkIndex } = data;
const session = this.uploadSessions.get(fileId);
if (!session) {
this.sendResponse(ws, MessageTypes.FILE_UPLOAD_RESULT, requestId, { success: false, error: 'Upload session not found' });
return;
}
let chunkData;
if (data.data) {
chunkData = Buffer.from(data.data, 'base64');
} else if (data.buffer) {
chunkData = Buffer.from(data.buffer);
}
if (!chunkData) {
this.sendResponse(ws, MessageTypes.FILE_UPLOAD_RESULT, requestId, { success: false, error: 'No chunk data provided' });
return;
}
session.chunks.set(chunkIndex, chunkData);
this.sendResponse(ws, MessageTypes.FILE_UPLOAD_CHUNK, requestId, { success: true, chunkIndex });
} catch (error) {
logger.error('Failed to upload chunk', { error: error.message });
this.sendResponse(ws, MessageTypes.FILE_UPLOAD_RESULT, requestId, { success: false, error: error.message });
}
}
handleFileUploadMerge(ws, data, requestId) {
try {
const { fileId, filename } = data;
const session = this.uploadSessions.get(fileId);
if (!session) {
this.sendResponse(ws, MessageTypes.FILE_UPLOAD_RESULT, requestId, { success: false, error: 'Upload session not found' });
return;
}
const success = fileService.mergeChunks(fileId, session.totalChunks, filename);
this.uploadSessions.delete(fileId);
if (success) {
fileService.cleanupChunks(fileId);
this.sendResponse(ws, MessageTypes.FILE_UPLOAD_RESULT, requestId, { success: true, filename });
} else {
fileService.cleanupChunks(fileId);
this.sendResponse(ws, MessageTypes.FILE_UPLOAD_RESULT, requestId, { success: false, error: 'Failed to merge chunks' });
}
} catch (error) {
logger.error('Failed to merge chunks', { error: error.message });
this.sendResponse(ws, MessageTypes.FILE_UPLOAD_RESULT, requestId, { success: false, error: error.message });
}
}
handleFileDownload(ws, data, requestId) {
try {
const { filename, filePath, allowSystem } = data;
let fullFilePath;
if (allowSystem && filePath && (path.isAbsolute(filePath) || filePath.includes(':') || filePath.startsWith('\\') || filePath.startsWith('/'))) {
if (path.isAbsolute(filePath)) {
fullFilePath = filePath;
} else {
fullFilePath = filePath.replace(/\//g, '\\');
}
} else if (filePath) {
fullFilePath = path.join(fileService.uploadDir, filePath);
} else {
fullFilePath = path.join(fileService.uploadDir, filename);
}
if (!fs.existsSync(fullFilePath)) {
this.sendResponse(ws, MessageTypes.FILE_DOWNLOAD_COMPLETE, requestId, { success: false, error: 'File not found: ' + fullFilePath });
return;
}
const stat = fs.statSync(fullFilePath);
const fileSize = stat.size;
const totalChunks = Math.ceil(fileSize / CHUNK_SIZE);
this.sendResponse(ws, MessageTypes.FILE_DOWNLOAD_START, requestId, {
filename,
size: fileSize,
chunkSize: CHUNK_SIZE,
totalChunks
});
const stream = fs.createReadStream(fullFilePath);
let chunkIndex = 0;
stream.on('data', (chunk) => {
ws.send(JSON.stringify({
type: MessageTypes.FILE_DOWNLOAD_CHUNK,
chunkIndex,
data: chunk.toString('base64'),
progress: Math.round(((chunkIndex + 1) / totalChunks) * 100)
}));
chunkIndex++;
});
stream.on('end', () => {
ws.send(JSON.stringify({
type: MessageTypes.FILE_DOWNLOAD_COMPLETE,
success: true,
filename
}));
});
stream.on('error', (error) => {
logger.error('File download error', { error: error.message });
ws.send(JSON.stringify({
type: MessageTypes.FILE_DOWNLOAD_COMPLETE,
success: false,
error: error.message
}));
});
} catch (error) {
logger.error('Failed to start download', { error: error.message });
this.sendResponse(ws, MessageTypes.FILE_DOWNLOAD_COMPLETE, requestId, { success: false, error: error.message });
}
}
handleFileDelete(ws, data, requestId) {
try {
const { filename, filePath } = data;
const targetPath = filePath || '';
const success = fileService.deleteFile(filename, targetPath);
this.sendResponse(ws, MessageTypes.FILE_DELETE_RESULT, requestId, { success, filename });
} catch (error) {
logger.error('Failed to delete file', { error: error.message });
this.sendResponse(ws, MessageTypes.FILE_DELETE_RESULT, requestId, { success: false, error: error.message });
}
}
cleanup() {
const now = Date.now();
const maxAge = 30 * 60 * 1000;
for (const [fileId, session] of this.uploadSessions) {
if (now - session.createdAt > maxAge) {
this.uploadSessions.delete(fileId);
}
}
}
}
module.exports = FileHandler;

View File

@@ -0,0 +1,78 @@
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const logger = require('../utils/logger');
const FileHandler = require('./FileHandler');
class FileServer {
constructor(port = 3001) {
this.port = port;
this.app = express();
this.server = http.createServer(this.app);
this.wss = null;
this.fileHandler = new FileHandler();
}
start() {
return new Promise((resolve, reject) => {
this.server.listen({ port: this.port, host: '0.0.0.0' }, () => {
logger.info('File server started', { port: this.port });
this._setupWebSocket();
resolve({ port: this.port });
});
this.server.on('error', reject);
});
}
_setupWebSocket() {
this.wss = new WebSocket.Server({ server: this.server, path: '/ws' });
this.wss.on('connection', (ws, req) => {
logger.info('File client connected', { ip: req.socket.remoteAddress });
ws.on('message', (data, isBinary) => {
try {
if (isBinary) {
logger.warn('Received binary data on file WebSocket, ignoring');
return;
}
const dataStr = data.toString();
logger.info('Raw message received:', dataStr.substring(0, 300));
const message = JSON.parse(dataStr);
logger.info('File message parsed', { type: message.type, requestId: message.requestId });
this.fileHandler.handleMessage(message, ws);
} catch (error) {
logger.error('Failed to parse file message', { error: error.message });
}
});
ws.on('close', () => {
logger.info('File client disconnected');
});
ws.on('error', (error) => {
logger.error('File WebSocket error', { error: error.message });
});
});
this.wss.on('error', (error) => {
logger.error('File WebSocket server error', { error: error.message });
});
}
stop() {
return new Promise((resolve, reject) => {
if (this.wss) {
this.wss.clients.forEach(client => client.close());
this.wss.close();
}
this.server.close((err) => {
if (err) reject(err);
else resolve();
});
});
}
}
module.exports = FileServer;

View File

@@ -44,7 +44,8 @@ class FileService {
}
getFilePath(filename) {
const filePath = path.join(this.uploadDir, path.basename(filename));
if (!filename) return null;
const filePath = path.normalize(filename);
if (!fs.existsSync(filePath)) {
return null;
}
@@ -92,7 +93,13 @@ class FileService {
mergeChunks(fileId, totalChunks, filename) {
try {
const filePath = path.join(this.uploadDir, path.basename(filename));
const filePath = path.normalize(filename);
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
const fd = fs.openSync(filePath, 'w');
for (let i = 0; i < totalChunks; i++) {

22
remote/test-connect.js Normal file
View File

@@ -0,0 +1,22 @@
const WebSocket = require('ws');
console.log('Testing connection to remote...');
const ws = new WebSocket('ws://146.56.248.142:8083/ws?password=wzw20040525', {
handshakeTimeout: 5000
});
ws.on('open', () => {
console.log('Connected!');
ws.close();
process.exit(0);
});
ws.on('error', (err) => {
console.log('Error:', err.message);
process.exit(1);
});
setTimeout(() => {
console.log('Connection timeout');
process.exit(1);
}, 8000);

268
remote/test-file-api.js Normal file
View File

@@ -0,0 +1,268 @@
const http = require('http');
const https = require('https');
const BASE_URL = 'http://localhost:3000';
const PASSWORD = 'wzw20040525';
const CHUNK_SIZE = 5 * 1024 * 1024;
function request(options, body = null) {
return new Promise((resolve, reject) => {
const url = new URL(options.path, BASE_URL);
url.searchParams.set('password', PASSWORD);
const client = url.protocol === 'https:' ? https : http;
const reqOptions = {
hostname: url.hostname,
port: url.port,
path: url.pathname + url.search,
method: options.method,
headers: options.headers || {}
};
const req = client.request(reqOptions, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
resolve({ status: res.statusCode, data: JSON.parse(data) });
} catch {
resolve({ status: res.statusCode, data });
}
});
});
req.on('error', reject);
if (body) {
if (body instanceof FormData) {
req.write(body.getBuffer());
} else if (Buffer.isBuffer(body)) {
req.write(body);
} else {
req.write(JSON.stringify(body));
}
}
req.end();
});
}
async function testGetDrives() {
console.log('\n=== 测试1: 获取驱动器列表 ===');
try {
const driveUrl = new URL('/api/files/browse', BASE_URL);
driveUrl.searchParams.set('allowSystem', 'true');
driveUrl.searchParams.set('password', PASSWORD);
const res = await request({
method: 'GET',
path: driveUrl.pathname + driveUrl.search
});
console.log('状态:', res.status);
console.log('currentPath:', res.data.currentPath);
console.log('parentPath:', res.data.parentPath);
// 检查是否返回了驱动器(盘符如 C:, D:
const drives = res.data.items?.filter(item => item.name.match(/^[A-Z]:$/i));
if (drives && drives.length > 0) {
console.log('✓ 驱动器列表:', drives.map(d => d.name).join(', '));
return true;
}
// 如果返回的是目录列表而非驱动器,说明 allowSystem 未生效
console.log('✗ 未获取到驱动器列表');
console.log('返回的项目:', res.data.items?.slice(0, 5).map(i => i.name).join(', '));
return false;
} catch (err) {
console.error('错误:', err.message);
return false;
}
}
async function testUploadToSystemDir() {
console.log('\n=== 测试2: 上传到系统目录 ===');
const testContent = 'Hello Remote System Directory Test ' + Date.now();
const filename = 'D:\\xc_test_file.txt';
try {
// 1. 开始上传
console.log('1. 开始上传...');
const startUrl = new URL('/api/files/upload/start', BASE_URL);
startUrl.searchParams.set('password', PASSWORD);
const startRes = await new Promise((resolve, reject) => {
const req = http.request({
hostname: startUrl.hostname,
port: startUrl.port,
path: startUrl.pathname + startUrl.search,
method: 'POST',
headers: { 'Content-Type': 'application/json' }
}, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try { resolve({ status: res.statusCode, data: JSON.parse(data) }); }
catch { resolve({ status: res.statusCode, data }); }
});
});
req.on('error', reject);
req.write(JSON.stringify({
filename: filename,
totalChunks: 1,
fileSize: Buffer.byteLength(testContent)
}));
req.end();
});
console.log('开始上传响应:', startRes.data);
if (!startRes.data.fileId) {
console.error('未获取到fileId');
return false;
}
const fileId = startRes.data.fileId;
// 2. 上传分块
console.log('2. 上传分块...');
const chunk = Buffer.from(testContent);
const chunkUrl = new URL('/api/files/upload/chunk', BASE_URL);
chunkUrl.searchParams.set('password', PASSWORD);
const FormData = require('form-data');
const form = new FormData();
form.append('fileId', fileId);
form.append('chunkIndex', '0');
form.append('chunk', chunk, { filename: 'chunk', contentType: 'application/octet-stream' });
const chunkRes = await new Promise((resolve, reject) => {
const req = http.request({
hostname: chunkUrl.hostname,
port: chunkUrl.port,
path: chunkUrl.pathname + chunkUrl.search,
method: 'POST',
headers: form.getHeaders()
}, (res) => {
let data = '';
res.on('data', c => data += c);
res.on('end', () => {
try { resolve({ status: res.statusCode, data: JSON.parse(data) }); }
catch { resolve({ status: res.statusCode, data }); }
});
});
req.on('error', reject);
req.write(form.getBuffer());
req.end();
});
console.log('分块上传响应:', chunkRes.data);
// 3. 合并文件
console.log('3. 合并文件...');
const mergeUrl = new URL('/api/files/upload/merge', BASE_URL);
mergeUrl.searchParams.set('password', PASSWORD);
const mergeRes = await new Promise((resolve, reject) => {
const req = http.request({
hostname: mergeUrl.hostname,
port: mergeUrl.port,
path: mergeUrl.pathname + mergeUrl.search,
method: 'POST',
headers: { 'Content-Type': 'application/json' }
}, (res) => {
let data = '';
res.on('data', c => data += c);
res.on('end', () => {
try { resolve({ status: res.statusCode, data: JSON.parse(data) }); }
catch { resolve({ status: res.statusCode, data }); }
});
});
req.on('error', reject);
req.write(JSON.stringify({
fileId: fileId,
totalChunks: 1,
filename: filename
}));
req.end();
});
console.log('合并响应:', mergeRes.data);
return mergeRes.data.success === true;
} catch (err) {
console.error('错误:', err.message);
return false;
}
}
async function testDownloadFromSystemDir() {
console.log('\n=== 测试3: 从系统目录下载 ===');
const filename = 'D:\\xc_test_file.txt';
try {
const encodedPath = encodeURIComponent(filename);
const res = await request({
method: 'GET',
path: `/api/files/${encodedPath}`
});
console.log('状态:', res.status);
console.log('响应类型:', typeof res.data);
return res.status === 200;
} catch (err) {
console.error('错误:', err.message);
return false;
}
}
async function testDeleteFile() {
console.log('\n=== 测试4: 删除测试文件 ===');
const filename = 'D:\\xc_test_file.txt';
try {
const encodedPath = encodeURIComponent(filename);
const res = await request({
method: 'DELETE',
path: `/api/files/${encodedPath}`
});
console.log('状态:', res.status);
console.log('响应:', res.data);
return res.data.success === true || res.status === 200;
} catch (err) {
console.error('错误:', err.message);
return false;
}
}
async function main() {
console.log('========================================');
console.log('远程文件传输功能测试');
console.log('目标服务器: 146.56.248.142:8080');
console.log('========================================');
const results = [];
// 测试1: 获取驱动器列表
results.push({ name: '获取驱动器列表', pass: await testGetDrives() });
// 测试2: 上传到系统目录
results.push({ name: '上传到系统目录', pass: await testUploadToSystemDir() });
// 测试3: 从系统目录下载
results.push({ name: '从系统目录下载', pass: await testDownloadFromSystemDir() });
// 测试4: 删除测试文件
results.push({ name: '删除测试文件', pass: await testDeleteFile() });
// 汇总结果
console.log('\n========================================');
console.log('测试结果汇总:');
console.log('========================================');
results.forEach(r => {
console.log(`${r.pass ? '✓' : '✗'} ${r.name}`);
});
const allPass = results.every(r => r.pass);
console.log(`\n总体结果: ${allPass ? '全部通过' : '存在失败'}`);
process.exit(allPass ? 0 : 1);
}
main();

89
remote/test-full.js Normal file
View File

@@ -0,0 +1,89 @@
const WebSocket = require('ws');
const fs = require('fs');
const ws = new WebSocket('ws://127.0.0.1:3003/ws?password=wzw20040525');
let fileId = 'upload_test_' + Date.now();
const testContent = Buffer.from('Hello World Test File Content');
ws.on('open', () => {
console.log('=== Connected, starting upload test ===');
ws.send(JSON.stringify({
type: 'fileUploadStart',
fileId: fileId,
filename: 'test.txt',
totalChunks: 1,
fileSize: testContent.length,
requestId: 'req1'
}));
});
ws.on('message', (data, isBinary) => {
if (isBinary) {
console.log('Binary data received');
return;
}
const msg = JSON.parse(data.toString());
console.log('Received:', msg.type, msg.fileId || '');
if (msg.type === 'fileUploadStart') {
console.log('Session started, sending chunk...');
ws.send(JSON.stringify({
type: 'fileUploadChunk',
fileId: fileId,
chunkIndex: 0,
data: testContent.toString('base64'),
requestId: 'req2'
}));
}
if (msg.type === 'fileUploadChunk') {
console.log('Chunk sent, sending merge...');
ws.send(JSON.stringify({
type: 'fileUploadMerge',
fileId: fileId,
filename: 'test.txt',
totalChunks: 1,
requestId: 'req3'
}));
}
if (msg.type === 'fileUploadResult') {
console.log('=== Upload Result:', msg.success ? 'SUCCESS' : 'FAILED', msg.filename || msg.error);
if (msg.success) {
console.log('\n=== Testing download ===');
ws.send(JSON.stringify({
type: 'fileDownloadStart',
filename: 'test.txt',
filePath: 'test.txt',
allowSystem: true,
requestId: 'req4'
}));
} else {
ws.close();
process.exit(1);
}
}
if (msg.type === 'fileDownloadStart') {
console.log('Download started, size:', msg.size);
}
if (msg.type === 'fileDownloadChunk') {
console.log('Download chunk:', msg.chunkIndex, 'progress:', msg.progress + '%');
}
if (msg.type === 'fileDownloadComplete') {
console.log('=== Download Result:', msg.success ? 'SUCCESS' : 'FAILED');
console.log('=== ALL TESTS PASSED ===');
ws.close();
process.exit(0);
}
});
ws.on('error', (err) => { console.error('Error:', err.message); });
setTimeout(() => { console.log('=== TIMEOUT ==='); process.exit(1); }, 20000);

13
remote/test-ports.js Normal file
View File

@@ -0,0 +1,13 @@
const WebSocket = require('ws');
console.log('Testing port 8080...');
const ws1 = new WebSocket('ws://146.56.248.142:8080/ws?password=wzw20040525');
ws1.on('open', () => console.log('8080: Connected'));
ws1.on('error', () => console.log('8080: Failed'));
ws1.on('close', () => {
console.log('Testing port 8083...');
const ws2 = new WebSocket('ws://146.56.248.142:8083/ws?password=wzw20040525');
ws2.on('open', () => console.log('8083: Connected'));
ws2.on('error', () => console.log('8083: Failed'));
setTimeout(() => process.exit(0), 3000);
});

71
remote/test-remote.js Normal file
View File

@@ -0,0 +1,71 @@
const WebSocket = require('ws');
const ws = new WebSocket('ws://146.56.248.142:8083/ws?password=wzw20040525');
let fileId = 'test_' + Date.now();
const testContent = Buffer.from('Hello Remote Test');
ws.on('open', () => {
console.log('=== Connected to 146.56.248.142:8083 ===');
ws.send(JSON.stringify({
type: 'fileUploadStart',
fileId: fileId,
filename: 'test.txt',
totalChunks: 1,
fileSize: testContent.length,
requestId: 'req1'
}));
});
ws.on('message', (data, isBinary) => {
if (isBinary) return;
const msg = JSON.parse(data.toString());
console.log('Received:', msg.type);
if (msg.type === 'fileUploadStart') {
ws.send(JSON.stringify({
type: 'fileUploadChunk',
fileId: fileId,
chunkIndex: 0,
data: testContent.toString('base64'),
requestId: 'req2'
}));
}
if (msg.type === 'fileUploadChunk') {
ws.send(JSON.stringify({
type: 'fileUploadMerge',
fileId: fileId,
filename: 'test.txt',
totalChunks: 1,
requestId: 'req3'
}));
}
if (msg.type === 'fileUploadResult') {
console.log('=== Upload:', msg.success ? 'SUCCESS' : 'FAILED');
if (msg.success) {
ws.send(JSON.stringify({
type: 'fileDownloadStart',
filename: 'test.txt',
filePath: 'test.txt',
allowSystem: false,
requestId: 'req4'
}));
} else {
ws.close();
process.exit(1);
}
}
if (msg.type === 'fileDownloadComplete') {
console.log('=== Download:', msg.success ? 'SUCCESS' : 'FAILED');
console.log('=== ALL TESTS PASSED ===');
ws.close();
process.exit(0);
}
});
ws.on('error', err => console.error('Error:', err.message));
setTimeout(() => { console.log('TIMEOUT'); process.exit(1); }, 15000);

30
remote/test-simple.js Normal file
View File

@@ -0,0 +1,30 @@
const WebSocket = require('ws');
console.log('Starting...');
const ws = new WebSocket('ws://146.56.248.142:8083/ws?password=wzw20040525');
ws.on('open', () => {
console.log('Connected!');
let fileId = 'test_' + Date.now();
const testContent = Buffer.from('Hello');
console.log('Sending fileUploadStart...');
ws.send(JSON.stringify({
type: 'fileUploadStart',
fileId: fileId,
filename: 'F:/test.txt',
totalChunks: 1,
fileSize: 5,
requestId: 'req1'
}));
});
ws.on('message', (data, isBinary) => {
console.log('Got message:', isBinary ? 'binary' : data.toString().substring(0,80));
});
ws.on('error', e => console.log('Error:', e.message));
ws.on('close', () => console.log('Closed'));
setTimeout(() => { console.log('Timeout'); process.exit(1); }, 10000);

57
remote/test-upload.js Normal file
View File

@@ -0,0 +1,57 @@
const WebSocket = require('ws');
console.log('Testing upload to remote 8083...');
const ws = new WebSocket('ws://146.56.248.142:8083/ws?password=wzw20040525');
let fileId = 'test_' + Date.now();
const testContent = Buffer.from('Hello Test 中文');
ws.on('open', () => {
console.log('Connected, sending fileUploadStart with F:/xxx.txt...');
ws.send(JSON.stringify({
type: 'fileUploadStart',
fileId: fileId,
filename: 'F:/小问题.txt',
totalChunks: 1,
fileSize: testContent.length,
requestId: 'req1'
}));
});
ws.on('message', (data, isBinary) => {
if (isBinary) return;
const msg = JSON.parse(data.toString());
console.log('Received:', msg.type);
if (msg.type === 'fileUploadStart') {
console.log('Session started, sending chunk...');
ws.send(JSON.stringify({
type: 'fileUploadChunk',
fileId: fileId,
chunkIndex: 0,
data: testContent.toString('base64'),
requestId: 'req2'
}));
}
if (msg.type === 'fileUploadChunk') {
console.log('Chunk sent, sending merge...');
ws.send(JSON.stringify({
type: 'fileUploadMerge',
fileId: fileId,
filename: 'F:/小问题.txt',
totalChunks: 1,
requestId: 'req3'
}));
}
if (msg.type === 'fileUploadResult') {
console.log('=== Upload Result:', msg.success ? 'SUCCESS' : 'FAILED', msg.error || '');
ws.close();
process.exit(0);
}
});
ws.on('error', err => console.error('Error:', err.message));
setTimeout(() => { console.log('Timeout'); process.exit(1); }, 15000);

32
remote/test-ws.js Normal file
View File

@@ -0,0 +1,32 @@
const WebSocket = require('ws');
const ws = new WebSocket('ws://127.0.0.1:3001/ws?password=wzw20040525');
ws.on('open', () => {
console.log('Connected, sending fileBrowse...');
ws.send(JSON.stringify({ type: 'fileBrowse', path: 'C:\\', allowSystem: true, requestId: 'test1' }));
});
ws.on('message', (data) => {
console.log('Received raw:', data.toString().substring(0, 200));
if (Buffer.isBuffer(data) || data instanceof ArrayBuffer) {
console.log('Binary data');
return;
}
try {
const msg = JSON.parse(data);
console.log('Received:', msg.type);
if (msg.type === 'fileBrowseResult') {
console.log('Items:', msg.items?.slice(0,3));
ws.close();
process.exit(0);
}
} catch(e) {
console.log('Parse error:', e.message);
}
});
ws.on('error', (err) => { console.error('Error:', err.message); });
setTimeout(() => { console.log('Timeout'); process.exit(1); }, 10000);