部分 FileOutputStream 不起作用

FileOutputStream in parts does not work

我正在尝试为我的应用程序开发文件传输功能。我对文件传输的实现是以对象的形式分段发送文件,其中包含有关文件的信息以及发送的字节。但是,我注意到,如果我将所有接收到的字节保存在一个列表中,然后立即将其写入文件,我实际上只能写入文件。如果我尝试分段写入文件,它最终会得到一个空文件,就好像该文件根本没有被写入一样。

这是我读取原始文件然后分段发送的方法:

public void sendFile(File src) {
    try {

        BufferedInputStream is = new BufferedInputStream(new FileInputStream(src));
        Message msg = new Message(MType.FILE_OPEN, true);
        com.transmit(msg);
        byte[] buf = new byte[Utility.bufferSize];
        msg = new Message(MType.FILE_NAME, src.getName());
        msg.setValue(MType.FILE_SIZE, Files.size(src.toPath()));
        com.transmit(msg);
        for (int count = is.read(buf); count > 0; count = is.read(buf)) {

            msg = new Message(MType.FILE_NAME, src.getName());
            msg.setValue(MType.FILE_SIZE, Files.size(src.toPath()));
            msg.setValue(MType.FILE_BYTE, buf);
            msg.setValue(MType.FILE_COUNT, count);
            com.transmit(msg);

        }
        msg = new Message(MType.FILE_NAME, src.getName());
        msg.setValue(MType.FILE_SIZE, Files.size(src.toPath()));
        msg.setValue(MType.FILE_CLOSE, true);
        is.close();
        com.transmit(msg);

    } catch (IOException e) {
        sender.getChatRoomController().error(ProgramError.ATTACH_FILE);
        Utility.log(e);
        e.printStackTrace();
    }
}

这是我在另一端接收消息对象的方法:

public void readFile(Message msg) {

    if (msg.hasID(MType.FILE_NAME)) {
        String name = msg.getValue(MType.FILE_NAME).toString();
        long size = (long) msg.getValue(MType.FILE_SIZE);
        File file = new File(Directories.fDir.getDirectory(), name);
        TempFile tf = new TempFile(file);
        if (!map.containsKey(file)) {
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            map.put(file, tf);
        } else {
            tf = map.get(file);
        }
        if (msg.hasValue(MType.FILE_BYTE)) {
            byte[] buf = (byte[]) msg.getValue(MType.FILE_BYTE);
            int count = (int) msg.getValue(MType.FILE_COUNT);
            tf.addEntry(buf, count);
        }
        if (msg.hasValue(MType.FILE_CLOSE)) {
            tf.writeFile(true);
            map.remove(file);

            if (sender instanceof Server) {
                Server server = (Server) sender;
                msg = new Message(MType.FILE_NAME, name);
                msg.setValue(MType.FILE_SIZE, size);
                msg.setValue(MType.FILE_ATTACHMENT, server.getFileID());
                addFile(file, server);
                server.broadcast(msg);
            }

        }
    }
}

这是我的临时文件 class:

    public class TempFile {

    private ArrayList<Byte[]> data;
    private ArrayList<Integer> counts;
    private File file;

    public TempFile(File file) {
        data = new ArrayList<>();
        counts = new ArrayList<>();
        this.file = file;
    }

    public void addEntry(byte[] data, int count) {

        this.data.add(Utility.toWrapper(data));
        this.counts.add(count);

    }

    public void writeFile(boolean append) {
        try {
            BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));
            for (int i = 0; i < data.size(); i++) {
                byte[] chunk = Utility.toPrimitive(data.get(i));
                int count = counts.get(i);
                os.write(chunk, 0, count);
            }
            os.flush();
            os.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

这是我的另一个涉及实际临时文件的实现:

    public class TempFile2 {

    private File file;
    private File tempFile;
    private FileOutputStream os;

    public TempFile2(File file) {
        this.file = file;
        this.tempFile = new File(file.getParent(), FilenameUtils.getBaseName(file.getName()) + ".tmp");
        if (!tempFile.exists()) {
            try {
                tempFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            os = new FileOutputStream(tempFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public void addEntry(byte[] data, int count) {

        try {
            os.write(data, 0, count);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void writeFile() {

        try {
            os.close();
            Files.copy(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

要完成这项工作,您需要随时刷新输出流。

    public class TempFile2 {

    private File file;
    private File tempFile;
    private FileOutputStream os;

    public TempFile2(File file) {
        this.file = file;
        this.tempFile = new File(file.getParent(), FilenameUtils.getBaseName(file.getName()) + ".tmp");
        if (!tempFile.exists()) {
            try {
                tempFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            os = new FileOutputStream(tempFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public void addEntry(byte[] data, int count) {

        try {
            os.write(data, 0, count);
            os.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void writeFile() {

        try {
            os.close();
            Files.copy(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

事实上,您可以在每次收到数据块时打开和关闭输出流,而不是保持输出流打开:

    public class TempFile2 {

    private File file;
    private File tempFile;

    public TempFile2(File file) {
        this.file = file;
        this.tempFile = new File(file.getParent(), FilenameUtils.getBaseName(file.getName()) + ".tmp");
        if (!tempFile.exists()) {
            try {
                tempFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    public void addEntry(byte[] data, int count) {
        try(OutputStream os = new FileOutputStream(tempFile, true)) {
            os.write(data, 0, count);
            os.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void writeFile() {

        try {
            Files.copy(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

另外,我有一个建议。

read() 操作可能只读取几个字节,但您当前的实现无论如何都会发送整个数组。一种解决方案是创建一个新的较小的数组来保存数据:

byte[] bytesToSend = new byte[count];
System.arraycopy(buf, 0, bytesToSend, 0, count);

我认为更好的解决方案是在此处使用 Base64 class 并发送字节数组的序列化版本而不是字节数组本身。

// In sender
byte[] bytesToSend = new byte[count];
System.arraycopy(buf, 0, bytesToSend, 0, count);
String encodedBytes = Base64.getEncoder().encodeToString(bytesToSend);
msg.setValue(MType.FILE_BYTE, encodedBytes);

// In receiver
String encodedBytes = (String) msg.getValue(MType.FILE_BYTE);
buf = Base64.getDecoder().decode(encodedBytes);