Skip to content

Commit

Permalink
FileChannel map 2GB限制处理
Browse files Browse the repository at this point in the history
  • Loading branch information
monkeyWie committed Jan 4, 2018
1 parent 81122cd commit d00bf77
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 165 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</parent>
<groupId>lee.study</groupId>
<artifactId>proxyee-down</artifactId>
<version>1.6</version>
<version>1.6.1</version>

<build>
<plugins>
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/lee/study/down/HttpDownServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public class HttpDownServer implements InitializingBean, EmbeddedServletContaine

public static int VIEW_SERVER_PORT;
public static SslContext CLIENT_SSL_CONTEXT;
public static String ACTIVE;

public static String HOME_PATH;
public static String RECORD_PATH;
Expand Down Expand Up @@ -91,9 +92,14 @@ public class HttpDownServer implements InitializingBean, EmbeddedServletContaine
CONFIG_PATH = HOME_PATH + File.separator + "proxyee-down.cfg";
}

public static boolean isDev(){
return "dev".equals(ACTIVE);
}

@Override
public void afterPropertiesSet() throws Exception {
if ("dev".equals(active)) {
ACTIVE = active;
if (isDev()) {
VIEW_SERVER_PORT = viewServerPort;
ResourceLeakDetector.setLevel(Level.ADVANCED);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.LinkedList;
import java.util.List;
import lee.study.down.HttpDownServer;
import lee.study.down.ext.BdyZip;
import lee.study.down.form.DownForm;
import lee.study.down.form.UnzipForm;
import lee.study.down.model.ChunkInfo;
Expand All @@ -14,7 +15,6 @@
import lee.study.down.model.ResultInfo;
import lee.study.down.model.ResultInfo.ResultStatus;
import lee.study.down.model.TaskInfo;
import lee.study.down.util.ByteUtil;
import lee.study.down.util.HttpDownUtil;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
Expand Down Expand Up @@ -200,7 +200,7 @@ public ResultInfo deleteTask(@RequestParam String id) {
@RequestMapping("/bdyUnzip")
public ResultInfo bdyUnzip(@RequestBody UnzipForm unzipForm) throws Exception {
ResultInfo resultInfo = new ResultInfo();
ByteUtil.unzipBdy(unzipForm.getFilePath(), unzipForm.getToPath());
BdyZip.unzip(unzipForm.getFilePath(), unzipForm.getToPath());
return resultInfo;
}
}
126 changes: 126 additions & 0 deletions src/main/java/lee/study/down/ext/BdyZip.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package lee.study.down.ext;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import lee.study.down.util.ByteUtil;
import lee.study.down.util.FileUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BdyZip {

private static final Logger LOGGER = LoggerFactory.getLogger(BdyZip.class);

private static final byte[] ZIP_ENTRY_FILE_HEARD = new byte[]{0x50, 0x4B, 0x03, 0x04, 0x0A, 0x00,
0x00, 0x00, 0x00, 0x00};
private static final byte[] ZIP_ENTRY_DIR_HEARD = new byte[]{0x50, 0x4B, 0x01, 0x02, 0x00, 0x00,
0x0A, 0x00, 0x00, 0x00};

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public static class BdyZipEntry {
private byte[] header = new byte[4];
private byte[] version = new byte[2];
private byte[] general = new byte[2];
private byte[] method = new byte[2];
private byte[] time = new byte[2];
private byte[] date = new byte[2];
private byte[] crc32 = new byte[4];
private long compressedSize;
private long unCompressedSize;
private long fileNameLength;
private long extraFieldLength;
private String fileName;
private byte[] extraField;
private boolean isRepair;
}

public static BdyZipEntry getNextBdyZipEntry(FileChannel fileChannel) throws IOException {
BdyZipEntry zipEntry = new BdyZipEntry();
ByteBuffer buffer = ByteBuffer.allocate(30);
fileChannel.read(buffer);
buffer.flip();
buffer.get(zipEntry.getHeader());
buffer.get(zipEntry.getVersion());
buffer.get(zipEntry.getGeneral());
buffer.get(zipEntry.getMethod());
buffer.get(zipEntry.getTime());
buffer.get(zipEntry.getDate());
buffer.get(zipEntry.getCrc32());

byte[] bts4 = new byte[4];
buffer.get(bts4);
zipEntry.setCompressedSize(ByteUtil.btsToNumForSmall(bts4));
buffer.get(bts4);
zipEntry.setUnCompressedSize(ByteUtil.btsToNumForSmall(bts4));
byte[] bts2 = new byte[2];
buffer.get(bts2);
zipEntry.setFileNameLength(ByteUtil.btsToNumForSmall(bts2));
buffer.get(bts2);
zipEntry.setExtraFieldLength(ByteUtil.btsToNumForSmall(bts2));

ByteBuffer fileNameBuffer = ByteBuffer.allocate((int) zipEntry.getFileNameLength());
fileChannel.read(fileNameBuffer);
fileNameBuffer.flip();
zipEntry.setFileName(Charset.forName("GBK").decode(fileNameBuffer).toString());
if (zipEntry.getExtraFieldLength() > 0) {
ByteBuffer extraFieldBuffer = ByteBuffer.allocate((int) zipEntry.getExtraFieldLength());
fileChannel.read(extraFieldBuffer);
zipEntry.setExtraField(extraFieldBuffer.array());
}
return zipEntry;
}

public static void unzip(String path, String toPath) throws IOException {
File zipFile = new File(path);
File toDir = FileUtil.createDirSmart(toPath);
FileChannel fileChannel = new RandomAccessFile(zipFile, "rw").getChannel();
boolean isEnd = false;
while (!isEnd) {
BdyZipEntry bdyZipEntry = getNextBdyZipEntry(fileChannel);
long fileSize = bdyZipEntry.getCompressedSize();
if (ByteUtil.matchToken(fileChannel, fileChannel.position() + fileSize, ZIP_ENTRY_DIR_HEARD)) {
isEnd = true;
} else if (!ByteUtil.matchToken(fileChannel, fileChannel.position() + fileSize,
ZIP_ENTRY_FILE_HEARD)) {
//找到真实文件长度
fileSize = ByteUtil.getNextTokenSize(fileChannel, ZIP_ENTRY_FILE_HEARD, ZIP_ENTRY_DIR_HEARD);
if (ByteUtil.matchToken(fileChannel, fileChannel.position() + fileSize, ZIP_ENTRY_DIR_HEARD)) {
isEnd = true;
}
}
LOGGER.debug("bdyUnzip:"+bdyZipEntry.getFileName()+"\t"+fileSize+"\t"+isEnd);
if (fileSize == 0
&& bdyZipEntry.getFileName().lastIndexOf("/") == bdyZipEntry.getFileName().length() - 1) {
FileUtil.createDirSmart(toDir.getPath() + File.separator + bdyZipEntry.getFileName());
} else {
File unzipFile = FileUtil
.createFileSmart(toDir.getPath() + File.separator + bdyZipEntry.getFileName());
FileChannel unzipChannel = new RandomAccessFile(unzipFile, "rw").getChannel();
long position = fileChannel.position();
long remaining = fileSize;
while (remaining > 0) {
long transferred = fileChannel.transferTo(position, remaining, unzipChannel);
remaining -= transferred;
position += transferred;
}
unzipChannel.close();
fileChannel.position(fileChannel.position() + fileSize);
}
if (fileChannel.position() == fileChannel.size()) {
isEnd = true;
}
}
fileChannel.close();
}
}
70 changes: 70 additions & 0 deletions src/main/java/lee/study/down/ext/LargeMappedByteBuffer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package lee.study.down.ext;

import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.LinkedList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.nio.ch.FileChannelImpl;

public class LargeMappedByteBuffer implements Closeable {

private static final Logger LOGGER = LoggerFactory.getLogger(LargeMappedByteBuffer.class);

private List<MappedByteBuffer> bufferList;
private long rawPosition;
private long position;

public LargeMappedByteBuffer(FileChannel fileChannel, MapMode mapMode, long position, long size)
throws IOException {
this.rawPosition = position;
this.position = position;
int count = (int) Math.ceil(size / (double) Integer.MAX_VALUE);
this.bufferList = new LinkedList<>();
long calcPos = position;
for (int i = 0; i < count; i++) {
long calcSize = i + 1 == count ? size % Integer.MAX_VALUE : Integer.MAX_VALUE;
bufferList.add(fileChannel.map(mapMode, calcPos, calcSize));
calcPos += calcSize;
}
}

public final void put(ByteBuffer byteBuffer) {
int index = getIndex();
long length = byteBuffer.limit() - byteBuffer.position();
this.position += length;
MappedByteBuffer mappedBuffer = bufferList.get(index);
if (mappedBuffer.remaining() < length) {
byte[] temp = new byte[mappedBuffer.remaining()];
byteBuffer.get(temp);
bufferList.get(index).put(temp);
bufferList.get(index + 1).put(byteBuffer);
} else {
bufferList.get(index).put(byteBuffer);
}
}

private int getIndex() {
return (int) ((this.position - this.rawPosition) / Integer.MAX_VALUE);
}

@Override
public void close() throws IOException {
try {
Method m = FileChannelImpl.class.getDeclaredMethod("unmap", MappedByteBuffer.class);
m.setAccessible(true);
for (MappedByteBuffer mappedBuffer : bufferList) {
m.invoke(FileChannelImpl.class, mappedBuffer);
}
} catch (Exception e) {
LOGGER.error("LargeMappedByteBuffer close:",e);
}
}

}
11 changes: 4 additions & 7 deletions src/main/java/lee/study/down/hanndle/HttpDownInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
import java.nio.channels.FileChannel.MapMode;
import lee.study.down.HttpDownServer;
import lee.study.down.dispatch.HttpDownCallback;
import lee.study.down.ext.LargeMappedByteBuffer;
import lee.study.down.model.ChunkInfo;
import lee.study.down.model.TaskInfo;
import lee.study.down.util.FileUtil;
import lee.study.down.util.HttpDownUtil;

public class HttpDownInitializer extends ChannelInitializer {
Expand Down Expand Up @@ -127,8 +127,8 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
if (close == null || close == false) {
FileChannel fileChannel = new RandomAccessFile(taskInfo.buildTaskFilePath(), "rw")
.getChannel();
MappedByteBuffer mappedBuffer = fileChannel.map(MapMode.READ_WRITE,
chunkInfo.getOriStartPosition() + chunkInfo.getDownSize(),
LargeMappedByteBuffer mappedBuffer = new LargeMappedByteBuffer(fileChannel,
MapMode.READ_WRITE, chunkInfo.getOriStartPosition() + chunkInfo.getDownSize(),
chunkInfo.getTotalSize() - chunkInfo.getDownSize());
chunkInfo.setStatus(1);
chunkInfo.setFileChannel(fileChannel);
Expand All @@ -151,7 +151,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E
HttpDownServer.LOGGER.debug(
"服务器响应异常重试:" + chunkInfo.getIndex() + "\t" + chunkInfo.getDownSize());
} else {
HttpDownServer.LOGGER.error("down onError:", cause.fillInStackTrace());
HttpDownServer.LOGGER.error("down onError:", cause);
}
}

Expand All @@ -176,9 +176,6 @@ public static void main(String[] args) throws Exception {
FileChannel fileChannel = new RandomAccessFile(path, "rw").getChannel();
MappedByteBuffer mbb = fileChannel.map(MapMode.READ_WRITE, 0, 5);
mbb.put(new byte[]{1, 2, 3, 4, 5});
FileUtil.unmap(mbb);
fileChannel.close();
FileUtil.deleteIfExists(path);
}

}
3 changes: 2 additions & 1 deletion src/main/java/lee/study/down/model/ChunkInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.io.Serializable;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import lee.study.down.ext.LargeMappedByteBuffer;
import lombok.AllArgsConstructor;
import lombok.Data;

Expand All @@ -29,7 +30,7 @@ public class ChunkInfo implements Serializable {
@JsonIgnore
private transient volatile FileChannel fileChannel;
@JsonIgnore
private transient volatile MappedByteBuffer mappedBuffer;
private transient volatile LargeMappedByteBuffer mappedBuffer;

public ChunkInfo() {
}
Expand Down
Loading

0 comments on commit d00bf77

Please sign in to comment.