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