1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.core5.http.nio.entity;
28
29 import java.io.File;
30 import java.io.IOException;
31 import java.io.RandomAccessFile;
32 import java.nio.ByteBuffer;
33 import java.util.Collections;
34 import java.util.Set;
35 import java.util.concurrent.atomic.AtomicReference;
36
37 import org.apache.hc.core5.http.ContentType;
38 import org.apache.hc.core5.http.nio.AsyncEntityProducer;
39 import org.apache.hc.core5.http.nio.DataStreamChannel;
40 import org.apache.hc.core5.io.Closer;
41 import org.apache.hc.core5.util.Args;
42 import org.apache.hc.core5.util.Asserts;
43
44
45
46
47
48
49
50 public final class FileEntityProducer implements AsyncEntityProducer {
51
52 private final File file;
53 private final ByteBuffer byteBuffer;
54 private final long length;
55 private final ContentType contentType;
56 private final boolean chunked;
57 private final AtomicReference<Exception> exception;
58 private final AtomicReference<RandomAccessFile> accessFileRef;
59 private boolean eof;
60
61 public FileEntityProducer(final File file, final int bufferSize, final ContentType contentType, final boolean chunked) {
62 this.file = Args.notNull(file, "File");
63 this.length = file.length();
64 this.byteBuffer = ByteBuffer.allocate(bufferSize);
65 this.contentType = contentType;
66 this.chunked = chunked;
67 this.accessFileRef = new AtomicReference<>();
68 this.exception = new AtomicReference<>();
69 }
70
71 public FileEntityProducer(final File file, final ContentType contentType, final boolean chunked) {
72 this(file, 8192, contentType, chunked);
73 }
74
75 public FileEntityProducer(final File file, final ContentType contentType) {
76 this(file, contentType, false);
77 }
78
79 public FileEntityProducer(final File file) {
80 this(file, ContentType.APPLICATION_OCTET_STREAM);
81 }
82
83 @Override
84 public boolean isRepeatable() {
85 return true;
86 }
87
88 @Override
89 public String getContentType() {
90 return contentType != null ? contentType.toString() : null;
91 }
92
93 @Override
94 public long getContentLength() {
95 return length;
96 }
97
98 @Override
99 public int available() {
100 return Integer.MAX_VALUE;
101 }
102
103 @Override
104 public String getContentEncoding() {
105 return null;
106 }
107
108 @Override
109 public boolean isChunked() {
110 return chunked;
111 }
112
113 @Override
114 public Set<String> getTrailerNames() {
115 return Collections.emptySet();
116 }
117
118 @Override
119 public void produce(final DataStreamChannel channel) throws IOException {
120 @SuppressWarnings("resource")
121 RandomAccessFile accessFile = accessFileRef.get();
122 if (accessFile == null) {
123 accessFile = new RandomAccessFile(file, "r");
124 Asserts.check(accessFileRef.getAndSet(accessFile) == null, "Illegal producer state");
125 }
126 if (!eof) {
127 final int bytesRead = accessFile.getChannel().read(byteBuffer);
128 if (bytesRead < 0) {
129 eof = true;
130 }
131 }
132 if (byteBuffer.position() > 0) {
133 byteBuffer.flip();
134 channel.write(byteBuffer);
135 byteBuffer.compact();
136 }
137 if (eof && byteBuffer.position() == 0) {
138 channel.endStream();
139 releaseResources();
140 }
141 }
142
143 @Override
144 public void failed(final Exception cause) {
145 if (exception.compareAndSet(null, cause)) {
146 releaseResources();
147 }
148 }
149
150 public Exception getException() {
151 return exception.get();
152 }
153
154 @Override
155 public void releaseResources() {
156 eof = false;
157 Closer.closeQuietly(accessFileRef.getAndSet(null));
158 }
159
160 }