1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
27
28 package org.apache.http.entity;
29
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33
34 import org.apache.http.annotation.NotThreadSafe;
35 import org.apache.http.util.Args;
36
37 /**
38 * A streamed, non-repeatable entity that obtains its content from
39 * an {@link InputStream}.
40 *
41 * @since 4.0
42 */
43 @NotThreadSafe
44 public class InputStreamEntity extends AbstractHttpEntity {
45
46 private final InputStream content;
47 private final long length;
48
49 /**
50 * Creates an entity with an unknown length.
51 * Equivalent to {@code new InputStreamEntity(instream, -1)}.
52 *
53 * @param instream
54 * @throws IllegalArgumentException if {@code instream} is {@code null}
55 * @since 4.3
56 */
57 public InputStreamEntity(final InputStream instream) {
58 this(instream, -1);
59 }
60
61 /**
62 * Creates an entity with a specified content length.
63 *
64 * @param instream
65 * @param length of the input stream, {@code -1} if unknown
66 * @throws IllegalArgumentException if {@code instream} is {@code null}
67 */
68 public InputStreamEntity(final InputStream instream, final long length) {
69 this(instream, length, null);
70 }
71
72 /**
73 * Creates an entity with a content type and unknown length.
74 * Equivalent to {@code new InputStreamEntity(instream, -1, contentType)}.
75 *
76 * @param instream
77 * @param contentType
78 * @throws IllegalArgumentException if {@code instream} is {@code null}
79 * @since 4.3
80 */
81 public InputStreamEntity(final InputStream instream, final ContentType contentType) {
82 this(instream, -1, contentType);
83 }
84
85 /**
86 * @param instream
87 * @param length of the input stream, {@code -1} if unknown
88 * @param contentType for specifying the {@code Content-Type} header, may be {@code null}
89 * @throws IllegalArgumentException if {@code instream} is {@code null}
90 * @since 4.2
91 */
92 public InputStreamEntity(final InputStream instream, final long length, final ContentType contentType) {
93 super();
94 this.content = Args.notNull(instream, "Source input stream");
95 this.length = length;
96 if (contentType != null) {
97 setContentType(contentType.toString());
98 }
99 }
100
101 public boolean isRepeatable() {
102 return false;
103 }
104
105 /**
106 * @return the content length or {@code -1} if unknown
107 */
108 public long getContentLength() {
109 return this.length;
110 }
111
112 public InputStream getContent() throws IOException {
113 return this.content;
114 }
115
116 /**
117 * Writes bytes from the {@code InputStream} this entity was constructed
118 * with to an {@code OutputStream}. The content length
119 * determines how many bytes are written. If the length is unknown ({@code -1}), the
120 * stream will be completely consumed (to the end of the stream).
121 *
122 */
123 public void writeTo(final OutputStream outstream) throws IOException {
124 Args.notNull(outstream, "Output stream");
125 final InputStream instream = this.content;
126 try {
127 final byte[] buffer = new byte[OUTPUT_BUFFER_SIZE];
128 int l;
129 if (this.length < 0) {
130 // consume until EOF
131 while ((l = instream.read(buffer)) != -1) {
132 outstream.write(buffer, 0, l);
133 }
134 } else {
135 // consume no more than length
136 long remaining = this.length;
137 while (remaining > 0) {
138 l = instream.read(buffer, 0, (int)Math.min(OUTPUT_BUFFER_SIZE, remaining));
139 if (l == -1) {
140 break;
141 }
142 outstream.write(buffer, 0, l);
143 remaining -= l;
144 }
145 }
146 } finally {
147 instream.close();
148 }
149 }
150
151 public boolean isStreaming() {
152 return true;
153 }
154
155 /**
156 * @deprecated (4.1) Either use {@link #getContent()} and call {@link java.io.InputStream#close()} on that;
157 * otherwise call {@link #writeTo(OutputStream)} which is required to free the resources.
158 */
159 @Deprecated
160 @Override
161 public void consumeContent() throws IOException {
162 // If the input stream is from a connection, closing it will read to
163 // the end of the content. Otherwise, we don't care what it does.
164 this.content.close();
165 }
166
167 }