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