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.io.RandomAccessFile;
25 import java.nio.channels.FileChannel;
26
27 import static java.util.Objects.requireNonNull;
28
29
30
31
32
33
34
35
36 public class ChunkedFile implements ChunkedInput<Buffer> {
37
38 private final RandomAccessFile file;
39 private final FileChannel channel;
40 private final long startOffset;
41 private final long endOffset;
42 private final int chunkSize;
43 private long offset;
44
45
46
47
48 public ChunkedFile(File file) throws IOException {
49 this(file, ChunkedStream.DEFAULT_CHUNK_SIZE);
50 }
51
52
53
54
55
56
57
58 public ChunkedFile(File file, int chunkSize) throws IOException {
59 this(new RandomAccessFile(file, "r"), chunkSize);
60 }
61
62
63
64
65 public ChunkedFile(RandomAccessFile file) throws IOException {
66 this(file, ChunkedStream.DEFAULT_CHUNK_SIZE);
67 }
68
69
70
71
72
73
74
75 public ChunkedFile(RandomAccessFile file, int chunkSize) throws IOException {
76 this(file, 0, file.length(), chunkSize);
77 }
78
79
80
81
82
83
84
85
86
87 public ChunkedFile(RandomAccessFile file, long offset, long length, int chunkSize) throws IOException {
88 requireNonNull(file, "file");
89 if (offset < 0) {
90 throw new IllegalArgumentException(
91 "offset: " + offset + " (expected: 0 or greater)");
92 }
93 if (length < 0) {
94 throw new IllegalArgumentException(
95 "length: " + length + " (expected: 0 or greater)");
96 }
97 if (chunkSize <= 0) {
98 throw new IllegalArgumentException(
99 "chunkSize: " + chunkSize +
100 " (expected: a positive integer)");
101 }
102
103 this.file = file;
104 channel = file.getChannel();
105 this.offset = startOffset = offset;
106 endOffset = offset + length;
107 this.chunkSize = chunkSize;
108
109 file.seek(offset);
110 }
111
112
113
114
115 public long startOffset() {
116 return startOffset;
117 }
118
119
120
121
122 public long endOffset() {
123 return endOffset;
124 }
125
126
127
128
129 public long currentOffset() {
130 return offset;
131 }
132
133 @Override
134 public boolean isEndOfInput() throws Exception {
135 return !(offset < endOffset && file.getChannel().isOpen());
136 }
137
138 @SuppressWarnings("EmptyTryBlock")
139 @Override
140 public void close() throws Exception {
141 try (channel; file) {
142
143 }
144 }
145
146 @Override
147 public Buffer readChunk(BufferAllocator allocator) throws Exception {
148 long offset = this.offset;
149 if (offset >= endOffset) {
150 return null;
151 }
152
153 int chunkSize = (int) Math.min(this.chunkSize, endOffset - offset);
154
155
156 Buffer buf = allocator.allocate(chunkSize);
157 boolean release = true;
158 try {
159 int bytes = buf.transferFrom(channel, offset, chunkSize);
160 if (bytes != -1) {
161 this.offset = offset + bytes;
162 }
163 release = false;
164 return buf;
165 } finally {
166 if (release) {
167 buf.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 }