代码之家  ›  专栏  ›  技术社区  ›  Lexicographical

部分中的FileOutputStream不工作

  •  1
  • Lexicographical  · 技术社区  · 9 年前

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

    下面是我的方法,它读取原始文件,然后分部分发送:

    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();
        }
    }
    

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

    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);
                }
    
            }
        }
    }
    

    这是我的TempFile类:

        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();
            }
    
        }
    
    }
    
    1 回复  |  直到 9 年前
        1
  •  0
  •   Andrew Young    9 年前

    为了实现这一点,您需要在运行时刷新输出流。

        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类,发送字节数组的序列化版本,而不是字节数组本身。

    // 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);