nodejs如何实现简单的文件服务器

生来就可爱ヽ(ⅴ<●) 提交于 2020-03-13 05:44:16

先规定简单的传输协议

文件名的长度(两个字节) 文件名(长度不定,由前两个字节决定) 文件内容的长度(8个字节) 内容 (长度不定,由前8个字节决定)

 

使用nodejs 实现服务端

var net = require('net');
var fs  = require( 'fs' );
var server = net.createServer();

server.on('connection', function(client){
    console.log('client connected to server。IP:%s,port%s', client.remoteAddress, client.remotePort);
    var isFirst = true ;
    var fd ;

    var contentLength = 0 ;
    var dataTotalLength =0;

    var receiveData = Buffer.alloc(0);

    // data长度不可控,需要自己合并多个data直至指定的要求或者发送的socket发送FIN
    client.on('data', function(data){

        if (isFirst) {

            receiveData = Buffer.concat([receiveData, data]);

            if (receiveData.length < 2) {
                console.error("奇葩,data长度为%s", data.length)
                return ;
            }else {
                var start = 0;
                var end = 2;
                var buffer = Buffer.from(receiveData.slice(start, end));
                var filenamelength = buffer.readInt16BE();
                console.log("文件名长度为:%s" , filenamelength);
            }
            
            if (receiveData.length < filenamelength + 10) {
                console.error("奇葩,data长度为%s", data.length)
                return;
            }
            
            var start = end;
            var end = end + filenamelength;
            var buffer = Buffer.from(receiveData.slice(start, end));
            var filename = buffer.toString();
            console.log("文件名为:%s" ,filename);

            var start = end;
            var end = end + 8;
            var buffer = Buffer.from(receiveData.slice(start, end));
            contentLength = buffer.readBigInt64BE();
            console.log("文件长度为:%s" , contentLength);


            var newfilename = "data/" +  filename;
            fd = fs.openSync(newfilename, 'w+');
            isFirst = false;

            var var1 = receiveData.slice(end)
            fs.writeFileSync(fd, var1);
            dataTotalLength += receiveData.length - end;

        }else {
            fs.writeFileSync(fd, data);
            dataTotalLength += data.length;
        }

        if(dataTotalLength == contentLength){
            console.log("文件传输完毕");
        }
    });

    client.on("end", function () {
        console.log(dataTotalLength);
        console.log(contentLength);
        console.log("关闭socket!")
    });
    
    client.on("error",function (e) {
        console.error(e);
    })

});

server.listen(7251, '0.0.0.0');

 

虽然不是很喜欢这种弱类型的语言,但是nodejs与生俱来的的多线程特性还是让人震惊,以上实现中没有出现任何与Thread相关的关键词,但这却是一个支持多线程上传的服务。

 

java 客户端(多线程上传)

package test;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;


public class FileTransferClient{

    public static final String SERVER_IP = "127.0.0.1"; // 服务端IP
    public static final int SERVER_PORT = 7251; // 服务端端口

    public static void main(String[] args) throws Exception{

        String datadir = "C:\\Users\\who\\Desktop\\testdata\\data\\face" ;

        File directory = new File(datadir) ;
        assert directory.isDirectory() ;

        ExecutorService executor = Executors.newFixedThreadPool(10);

        int count = 0 ;

        for (File file : directory.listFiles()) {
            if(file.isFile()) {
                Task task = new Task(file.getAbsolutePath());
                executor.submit(task);
                count ++ ;
            }
        }

        while (true) {
            if (aCount.get() == count) {
                System.out.println("end");
                executor.shutdown();
                break;
            }
            Thread.sleep(1000);
        }
    }


    public static AtomicInteger aCount = new AtomicInteger(0);

}


class Task implements Runnable {

    private String filename;

    public Task(String filename){
        this.filename = filename ;
    }

    @Override
    public void run() {

        try {

            Socket socket = new Socket(FileTransferClient.SERVER_IP, FileTransferClient.SERVER_PORT);
            FileInputStream fis = null;
            DataOutputStream dos = null;

            File file = new File(filename);
            if (file.exists()) {
                fis = new FileInputStream(file);
                dos = new DataOutputStream(socket.getOutputStream());

                // 文件名和长度
                byte[] namebytes = file.getName().getBytes();
                dos.writeShort(namebytes.length);
                dos.write(namebytes);
                dos.writeLong(file.length());
                dos.flush();

                byte[] bytes = new byte[1024];
                int length;
                while ((length = fis.read(bytes, 0, bytes.length)) != -1) {
                    dos.write(bytes, 0, length);
                    dos.flush();
                }
            }

            if (fis != null)
                fis.close();
            if (dos != null)
                dos.close();

            if (!socket.isClosed()) {
                socket.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        FileTransferClient.aCount.addAndGet(1);
    }
}

 

nodejs 客户端

var net = require('net');
var fs = require("fs");


function sendfile(filename) {
    console.log(filename);

    var socket = new net.Socket();

    socket.on("ready",function () {
        var contentSize = 0 ;
        fs.stat(filename,function(error,stats){
            if(error){
                console.error(error)
            }else{
                contentSize = stats.size ;
                console.log(contentSize) ;
            }

            var length = 2 + filename.length +  8 ;
            var buffer = Buffer.alloc(length) ;

            buffer.writeInt16BE(filename.length,0);
            buffer.write(filename,2);

            var bigInt = BigInt(contentSize);
            var start = Number(2 + filename.length);
            buffer.writeBigInt64BE(bigInt,start);

            socket.write(buffer.slice(0,length)) ;
            // 大文件应该一次读取不了,不想写了
            var data = fs.readFileSync(filename);
            socket.write(data);
        }) ;
    }) ;


    socket.on("error",function (e) {
        console.error(e)
    }) ;

    socket.connect(7251, 'localhost');

}

sendfile("app.js");
sendfile("client.js");

 

 

nodejs 的 socket在处理字节流时不能主动的去read, 只能靠data事件来触发回调函数,但是data并不是一次能接收到所有的字节流,因此在处理时需要自己合并多个data才行,以下是一个小小的使用建议。

var net = require('net');

var client = new net.Socket();
var receiveData = Buffer.alloc(0);

client.connect(8899, 'localhost');

client.on('data', function (data) {
    // 这里只是合并data
    receiveData = Buffer.concat([receiveData, data]);
});

client.on('error', function (e) {
    console.error(e)
});

client.on('end', function (e) {
    // 接收完所有data再处理
    console.log(receiveData.toString());
});

 

 

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!