即我有一个10 MB的文件,我想删除1M到2M的字节,因此生成的文件是9mb,从文件中以2m字节开始的数据现在从1M开始
我正在使用Java 7,因此可以制作NIO,文件大小通常为10MB,并且经常通过网络访问,因此我正在寻找一种表现良好的优雅解决方案。
我知道BteBuffer.allocateDirect()和File.getChannel()但是我很难解决,如果有办法做我想要的,不需要从文件通道写入8MB到临时缓冲区只是为了将它写回到不同位置的文件,或者如果使用allocateDirect()
,这实际上是可以的答案 0 :(得分:3)
将结果写入临时文件,然后将旧文件替换为临时文件(用作磁盘上的缓冲区)。
代码示例:
public static void main(String[] args) {
// -- prepare files
File inFile = new File("D:/file.dat");
File outFile;
try {
outFile = File.createTempFile("swap", "buffer");
} catch (IOException ex) {
throw new IOError(ex);
}
// -- process file
try (
InputStream inStream = new FileInputStream(inFile);
OutputStream outStream = new FileOutputStream(outFile)
) {
//drop some bytes (will be removed)
inStream.skip(4);
//modify some bytes (will be changed)
for (int i = 0; i < 4; i++) {
byte b = (byte) inStream.read();
outStream.write(b >> 4);
}
//copy bytes in chunks (will be kept)
final int CHUNK_SIZE = 1024;
byte[] chunkBuffer = new byte[CHUNK_SIZE];
while (true) {
int chunkSize = inStream.read(chunkBuffer, 0, CHUNK_SIZE);
if (chunkSize < 0) {
break;
}
outStream.write(chunkBuffer, 0, chunkSize);
}
} catch (FileNotFoundException ex) {
throw new RuntimeException("input file not found!", ex);
} catch (IOException ex) {
throw new RuntimeException("failed to trim data!", ex);
}
// -- release changes
//replace inFile with outFile
inFile.delete();
outFile.renameTo(inFile);
}
答案 1 :(得分:0)
我不认为你可以将一个巨大的文件读入内存,然后切断它的某些部分。 @Binkan的方法更加智能,因为这个方法已经过高达15MB的文件测试
try {
// read the file
byte[] results = Files.readAllBytes(Paths.get("xfile.apk"));
// strip off say 1MB
byte[] trimmed = Arrays.copyOfRange(results, 0,
(results.length / (1024 * 1024)) - 1);
Files.write(Paths.get("xfile.apk"), trimmed,
StandardOpenOption.TRUNCATE_EXISTING);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
使用MappedByteBuffer可能会产生性能开销
答案 2 :(得分:0)
我认为在磁盘上使用临时文件的方法是一种很好的方法。如果您处于可以在磁盘上创建新临时文件的情况,那么nio确实有一些可以帮助您的选项。我刚刚在这里讨论API和nio教程,但似乎FileChannel.transferFrom
或FileChannel.transferTo
可能是您需要的工具。
我没有测试过以下代码,但它应该指向正确的方向。
public static void main(String[] args) {
int megaByte = 1024 * 1024;
// prepare the paths
Path inPath = Paths.get("D:/file.dat"); // java.nio.file.Paths
Path outPath; // java.nio.file.Path
try {
outPath = Files.createTempFile(null, "swp"); // java.nio.file.Files
} catch (IOException ex) {
throw new IOError(ex);
}
// process the file
try (
FileChannel readChannel = new FileChannel.open(inPath, READ);
FileChannel writeChannel = new FileChannel.open(outPath, WRITE, TRUNCATE_EXISTING)
) {
long readFileSize = readChannel.size();
long expectedWriteSize = readFileSize;
if (readFileSize > 2 * megabyte)
expectedWriteSize = readFileSize - megabyte;
else if (readFileSize > megabyte)
expectedWriteSize = megabyte;
// copy first megabyte (or entire file if less than a megabyte)
long bytesTrans = readChannel.transferTo(0, megabyte, writeChannel);
// copy everything after the second megabyte
if (readFileSize > 2 * megabyte)
bytesTrans += readChannel.transferTo(2 * megabyte, readFileSize - 2 * megabyte, writeChannel);
if (bytesTrans != expectedWriteSize)
System.out.println("WARNING: Transferred " + bytesTrans + " bytes instead of " + expectedWriteSize);
} catch (FileNotFoundException ex) {
throw new RuntimeException("File not found!", ex);
} catch (IOException ex) {
throw new RuntimeException("Caught IOException", ex);
}
// replace the original file with the temporary file
try {
// ATOMIC_MOVE may cause IOException here . . .
Files.move(outPath, inPath, REPLACE_EXISTING, ATOMIC_MOVE);
} catch (IOException e1) {
try {
// . . . so it's probably worth trying again without that option
Files.move(outPath, inPath, REPLACE_EXISTING);
} catch (IOException e2) {
throw new IOError(e2);
}
}
}
即使您无法打开新文件,nio也有可能提供帮助。如果您打开文件的读/写通道或在同一文件上打开两个不同的通道,可能会使用transferTo
方法将文件的一部分传输到文件的另一部分。我没有足够的经验来了解它。 API声明采用显式位置参数的方法(如transferTo
的第一个参数)可以与写入文件的操作同时进行,因此我不会将其排除。如果您尝试,可能需要以兆字节大小的块重写该文件。如果确实有效,那么FileChannel.truncate
可以用来在文件中将部分文件写入文件中的1兆字节位置时切断文件的最后一兆字节。