Java 部分中的FileOutputStream不工作
我正在为我的一个应用程序开发一个文件传输功能。我的文件传输实现是以对象的形式发送一个文件,其中包含有关文件的信息以及发送的字节。但是,我注意到,只有将所有接收到的字节保存在一个列表中,然后立即将其写入文件,我才能真正写入该文件。如果我尝试部分写入该文件,结果会是一个空文件,就好像该文件根本没有被写入一样 下面是我的方法,它读取原始文件,然后将其分部分发送:Java 部分中的FileOutputStream不工作,java,sockets,file-transfer,fileoutputstream,Java,Sockets,File Transfer,Fileoutputstream,我正在为我的一个应用程序开发一个文件传输功能。我的文件传输实现是以对象的形式发送一个文件,其中包含有关文件的信息以及发送的字节。但是,我注意到,只有将所有接收到的字节保存在一个列表中,然后立即将其写入文件,我才能真正写入该文件。如果我尝试部分写入该文件,结果会是一个空文件,就好像该文件根本没有被写入一样 下面是我的方法,它读取原始文件,然后将其分部分发送: public void sendFile(File src) { try { BufferedInputStrea
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);
}
}
}
}
下面是我的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);
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);
要使其工作,您需要在运行时刷新输出流
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);
.tmp文件和最终文件都是空的吗?您正在将所有条目添加到内存中的
ArrayList数据中
,然后当您在TempFile
类中实际写入OutputStream
时,您会迭代,并且只在循环中执行os.write
。您只能os.flush
在此循环之外。因此,是的,当您写入文件的所有条目或写入的数据超过内置限制时,它只会刷新写入OutputStream
缓冲区的内容。每次写入后进行刷新可能会导致代码的I/O性能下降,但是,可能在每n个条目之后进行刷新。tmp文件和最终文件都是空的吗?您正在将所有条目添加到内存中的ArrayList数据中,然后当您实际写入TempFile
类中的OutputStream
时,您迭代并只在循环中执行os.write
。您只能os.flush
在此循环之外。因此,是的,当您写入文件的所有条目或写入的数据超过内置限制时,它只会刷新写入OutputStream
缓冲区的内容。在每次写入之后进行刷新可能会对代码的I/O性能造成影响,但也可能在每n个条目之后进行刷新。