Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.aliyun.oss.common.comm.io; import static com.aliyun.oss.common.utils.LoggerUtils.logException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.http.entity.AbstractHttpEntity; import org.apache.http.entity.BasicHttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.util.Args; import com.aliyun.oss.common.comm.ServiceClient; import com.aliyun.oss.common.utils.HttpHeaders; /** * A repeatable HTTP entity that obtains its content from an non-auto-close * {@link ReleasableInputStreamEntity} and sends its content using chunked encoding. */ public class ChunkedInputStreamEntity extends BasicHttpEntity { private boolean firstAttempt = true; private ReleasableInputStreamEntity notClosableRequestEntity; private InputStream content; public ChunkedInputStreamEntity(ServiceClient.Request request) { setChunked(true); long contentLength = -1; try { String contentLengthString = request.getHeaders().get(HttpHeaders.CONTENT_LENGTH); if (contentLengthString != null) { contentLength = Long.parseLong(contentLengthString); } } catch (NumberFormatException nfe) { logException("Unable to parse content length from request.", nfe); } String contentType = request.getHeaders().get(HttpHeaders.CONTENT_TYPE); notClosableRequestEntity = new ReleasableInputStreamEntity(request.getContent(), contentLength); notClosableRequestEntity.setCloseDisabled(true); notClosableRequestEntity.setContentType(contentType); content = request.getContent(); setContent(content); setContentType(contentType); setContentLength(contentLength); } @Override public boolean isChunked() { return true; } @Override public boolean isRepeatable() { return content.markSupported() || notClosableRequestEntity.isRepeatable(); } @Override public void writeTo(OutputStream output) throws IOException { if (!firstAttempt && isRepeatable()) content.reset(); firstAttempt = false; notClosableRequestEntity.writeTo(output); } /** * A releaseable HTTP entity that can control its inner inputstream's auto-close functionality on/off, * and it will try to close its inner inputstream by default when the inputstream reaches EOF. */ public static class ReleasableInputStreamEntity extends AbstractHttpEntity implements Releasable { private final InputStream content; private final long length; private boolean closeDisabled; public ReleasableInputStreamEntity(final InputStream instream) { this(instream, -1); } public ReleasableInputStreamEntity(final InputStream instream, final long length) { this(instream, length, null); } public ReleasableInputStreamEntity(final InputStream instream, final ContentType contentType) { this(instream, -1, contentType); } public ReleasableInputStreamEntity(final InputStream instream, final long length, final ContentType contentType) { super(); this.content = Args.notNull(instream, "Source input stream"); this.length = length; if (contentType != null) { setContentType(contentType.toString()); } closeDisabled = false; } @Override public boolean isRepeatable() { return this.content.markSupported(); } @Override public long getContentLength() { return this.length; } @Override public InputStream getContent() throws IOException { return this.content; } @Override public void writeTo(final OutputStream outstream) throws IOException { Args.notNull(outstream, "Output stream"); final InputStream instream = this.content; try { final byte[] buffer = new byte[OUTPUT_BUFFER_SIZE]; int l; if (this.length < 0) { // consume until EOF while ((l = instream.read(buffer)) != -1) { outstream.write(buffer, 0, l); } } else { // consume no more than length long remaining = this.length; while (remaining > 0) { l = instream.read(buffer, 0, (int) Math.min(OUTPUT_BUFFER_SIZE, remaining)); if (l == -1) { break; } outstream.write(buffer, 0, l); remaining -= l; } } } finally { close(); } } @Override public boolean isStreaming() { return true; } public void close() { if (!closeDisabled) doRelease(); } @Override public void release() { doRelease(); } private void doRelease() { try { this.content.close(); } catch (Exception ex) { logException("Unexpected io exception when trying to close input stream", ex); } } public boolean isCloseDisabled() { return closeDisabled; } public void setCloseDisabled(boolean closeDisabled) { this.closeDisabled = closeDisabled; } } }