1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.stream;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufAllocator;
20 import io.netty.channel.ChannelHandlerContext;
21 import io.netty.channel.FileRegion;
22 import io.netty.util.internal.ObjectUtil;
23
24 import java.io.EOFException;
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.RandomAccessFile;
28 import java.nio.channels.ClosedChannelException;
29 import java.nio.channels.FileChannel;
30
31
32
33
34
35
36
37
38
39 public class ChunkedNioFile implements ChunkedInput<ByteBuf> {
40
41 private final FileChannel in;
42 private final long startOffset;
43 private final long endOffset;
44 private final int chunkSize;
45 private long offset;
46
47
48
49
50 public ChunkedNioFile(File in) throws IOException {
51 this(new RandomAccessFile(in, "r").getChannel());
52 }
53
54
55
56
57
58
59
60 public ChunkedNioFile(File in, int chunkSize) throws IOException {
61 this(new RandomAccessFile(in, "r").getChannel(), chunkSize);
62 }
63
64
65
66
67 public ChunkedNioFile(FileChannel in) throws IOException {
68 this(in, ChunkedStream.DEFAULT_CHUNK_SIZE);
69 }
70
71
72
73
74
75
76
77 public ChunkedNioFile(FileChannel in, int chunkSize) throws IOException {
78 this(in, 0, in.size(), chunkSize);
79 }
80
81
82
83
84
85
86
87
88
89 public ChunkedNioFile(FileChannel in, long offset, long length, int chunkSize)
90 throws IOException {
91 ObjectUtil.checkNotNull(in, "in");
92 ObjectUtil.checkPositiveOrZero(offset, "offset");
93 ObjectUtil.checkPositiveOrZero(length, "length");
94 ObjectUtil.checkPositive(chunkSize, "chunkSize");
95 if (!in.isOpen()) {
96 throw new ClosedChannelException();
97 }
98 this.in = in;
99 this.chunkSize = chunkSize;
100 this.offset = startOffset = offset;
101 endOffset = offset + length;
102 }
103
104
105
106
107 public long startOffset() {
108 return startOffset;
109 }
110
111
112
113
114 public long endOffset() {
115 return endOffset;
116 }
117
118
119
120
121 public long currentOffset() {
122 return offset;
123 }
124
125 @Override
126 public boolean isEndOfInput() throws Exception {
127 return !(offset < endOffset && in.isOpen());
128 }
129
130 @Override
131 public void close() throws Exception {
132 in.close();
133 }
134
135 @Deprecated
136 @Override
137 public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception {
138 return readChunk(ctx.alloc());
139 }
140
141 @Override
142 public ByteBuf readChunk(ByteBufAllocator allocator) throws Exception {
143 long offset = this.offset;
144 if (offset >= endOffset) {
145 return null;
146 }
147
148 int chunkSize = (int) Math.min(this.chunkSize, endOffset - offset);
149 ByteBuf buffer = allocator.buffer(chunkSize);
150 boolean release = true;
151 try {
152 int readBytes = 0;
153 for (;;) {
154 int localReadBytes = buffer.writeBytes(in, offset + readBytes, chunkSize - readBytes);
155 if (localReadBytes < 0) {
156
157
158
159 throw new EOFException("Underlying file size " + in.size() + " smaller than requested end " +
160 "offset " + endOffset);
161 }
162 readBytes += localReadBytes;
163 if (readBytes == chunkSize) {
164 break;
165 }
166 }
167 this.offset += readBytes;
168 release = false;
169 return buffer;
170 } finally {
171 if (release) {
172 buffer.release();
173 }
174 }
175 }
176
177 @Override
178 public long length() {
179 return endOffset - startOffset;
180 }
181
182 @Override
183 public long progress() {
184 return offset - startOffset;
185 }
186 }