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.hc.core5.http.io.entity; 29 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.OutputStream; 33 34 import org.apache.hc.core5.http.ContentType; 35 import org.apache.hc.core5.util.Args; 36 37 /** 38 * A streamed, non-repeatable entity that obtains its content from an {@link InputStream}. 39 * 40 * @since 4.0 41 */ 42 public class InputStreamEntity extends AbstractHttpEntity { 43 44 private final InputStream content; 45 private final long length; 46 47 /** 48 * Constructs a new instance with the given attributes kept as immutable. 49 * <p> 50 * The new instance: 51 * </p> 52 * <ul> 53 * <li>is not chunked.</li> 54 * </ul> 55 * 56 * @param content The message body contents as an InputStream. 57 * @param contentLength The value for the {@code Content-Length} header for the size of the message body, in bytes. 58 * @param contentType The content-type, may be null. 59 * @param contentEncoding The content encoding string, may be null. 60 */ 61 public InputStreamEntity( 62 final InputStream content, final long contentLength, final ContentType contentType, final String contentEncoding) { 63 super(contentType, contentEncoding); 64 this.content = Args.notNull(content, "Source input stream"); 65 this.length = contentLength; 66 } 67 68 /** 69 * Constructs a new instance with the given attributes kept as immutable. 70 * <p> 71 * The new instance: 72 * </p> 73 * <ul> 74 * <li>is not chunked.</li> 75 * <li>does not define a content encoding.</li> 76 * </ul> 77 * 78 * @param content The message body contents as an InputStream. 79 * @param contentLength The value for the {@code Content-Length} header for the size of the message body, in bytes. 80 * @param contentType The content-type, may be null. 81 */ 82 public InputStreamEntity(final InputStream content, final long contentLength, final ContentType contentType) { 83 this(content, contentLength, contentType, null); 84 } 85 86 /** 87 * Constructs a new instance with the given attributes kept as immutable. 88 * <p> 89 * The new instance: 90 * </p> 91 * <ul> 92 * <li>is not chunked.</li> 93 * <li>does not define a content length.</li> 94 * <li>does not define a content encoding.</li> 95 * </ul> 96 * 97 * @param content The message body contents as an InputStream. 98 * @param contentType The content-type, may be null. 99 */ 100 public InputStreamEntity(final InputStream content, final ContentType contentType) { 101 this(content, -1, contentType, null); 102 } 103 104 /** 105 * {@inheritDoc} 106 * <p> 107 * This implementation always returns {@code false}. 108 * </p> 109 */ 110 @Override 111 public final boolean isRepeatable() { 112 return false; 113 } 114 115 /** 116 * {@inheritDoc} 117 * 118 * @return the content length or {@code -1} if unknown. 119 */ 120 @Override 121 public final long getContentLength() { 122 return this.length; 123 } 124 125 @Override 126 public final InputStream getContent() throws IOException { 127 return this.content; 128 } 129 130 /** 131 * Writes bytes from the {@code InputStream} this entity was constructed 132 * with to an {@code OutputStream}. The content length 133 * determines how many bytes are written. If the length is unknown ({@code -1}), the 134 * stream will be completely consumed (to the end of the stream). 135 * 136 */ 137 @Override 138 public final void writeTo(final OutputStream outStream) throws IOException { 139 Args.notNull(outStream, "Output stream"); 140 try (final InputStream inStream = this.content) { 141 final byte[] buffer = new byte[OUTPUT_BUFFER_SIZE]; 142 int readLen; 143 if (this.length < 0) { 144 // consume until EOF 145 while ((readLen = inStream.read(buffer)) != -1) { 146 outStream.write(buffer, 0, readLen); 147 } 148 } else { 149 // consume no more than length 150 long remaining = this.length; 151 while (remaining > 0) { 152 readLen = inStream.read(buffer, 0, (int) Math.min(OUTPUT_BUFFER_SIZE, remaining)); 153 if (readLen == -1) { 154 break; 155 } 156 outStream.write(buffer, 0, readLen); 157 remaining -= readLen; 158 } 159 } 160 } 161 } 162 163 /** 164 * {@inheritDoc} 165 * <p> 166 * This implementation always returns {@code true}. 167 * </p> 168 */ 169 @Override 170 public final boolean isStreaming() { 171 return true; 172 } 173 174 @Override 175 public final void close() throws IOException { 176 content.close(); 177 } 178 179 }