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