001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.nio.entity; 029 030import java.io.File; 031import java.io.FileInputStream; 032import java.io.IOException; 033import java.io.InputStream; 034import java.io.OutputStream; 035import java.io.RandomAccessFile; 036import java.nio.channels.FileChannel; 037 038import org.apache.http.entity.AbstractHttpEntity; 039import org.apache.http.entity.ContentType; 040import org.apache.http.nio.ContentEncoder; 041import org.apache.http.nio.ContentEncoderChannel; 042import org.apache.http.nio.FileContentEncoder; 043import org.apache.http.nio.IOControl; 044import org.apache.http.util.Args; 045 046/** 047 * A self contained, repeatable non-blocking entity that retrieves its content 048 * from a file. This class is mostly used to stream large files of different 049 * types, so one needs to supply the content type of the file to make sure 050 * the content can be correctly recognized and processed by the recipient. 051 * 052 * @since 4.0 053 */ 054@SuppressWarnings("deprecation") 055public class NFileEntity extends AbstractHttpEntity 056 implements HttpAsyncContentProducer, ProducingNHttpEntity { 057 058 private final File file; 059 private RandomAccessFile accessfile; 060 private FileChannel fileChannel; 061 private long idx = -1; 062 private boolean useFileChannels; 063 064 /** 065 * Creates new instance of NFileEntity from the given source {@link File} 066 * with the given content type. If {@code useFileChannels} is set to 067 * {@code true}, the entity will try to use {@link FileContentEncoder} 068 * interface to stream file content directly from the file channel. 069 * 070 * @param file the source file. 071 * @param contentType the content type of the file. 072 * @param useFileChannels flag whether the direct transfer from the file 073 * channel should be attempted. 074 * 075 * @since 4.2 076 */ 077 public NFileEntity(final File file, final ContentType contentType, final boolean useFileChannels) { 078 Args.notNull(file, "File"); 079 this.file = file; 080 this.useFileChannels = useFileChannels; 081 if (contentType != null) { 082 setContentType(contentType.toString()); 083 } 084 } 085 086 /** 087 * @since 4.2 088 */ 089 public NFileEntity(final File file) { 090 Args.notNull(file, "File"); 091 this.file = file; 092 } 093 /** 094 * Creates new instance of NFileEntity from the given source {@link File} 095 * with the given content type. 096 * 097 * @param file the source file. 098 * @param contentType the content type of the file. 099 * 100 * @since 4.2 101 */ 102 public NFileEntity(final File file, final ContentType contentType) { 103 this(file, contentType, true); 104 } 105 106 /** 107 * @deprecated (4.2) use {@link #NFileEntity(File, ContentType, boolean)} 108 */ 109 @Deprecated 110 public NFileEntity(final File file, final String contentType, final boolean useFileChannels) { 111 Args.notNull(file, "File"); 112 this.file = file; 113 this.useFileChannels = useFileChannels; 114 setContentType(contentType); 115 } 116 117 /** 118 * @deprecated (4.2) use {@link #NFileEntity(File, ContentType)} 119 */ 120 @Deprecated 121 public NFileEntity(final File file, final String contentType) { 122 this(file, contentType, true); 123 } 124 125 /** 126 * {@inheritDoc} 127 * 128 * @since 4.2 129 */ 130 @Override 131 public void close() throws IOException { 132 if (accessfile != null) { 133 accessfile.close(); 134 } 135 accessfile = null; 136 fileChannel = null; 137 } 138 139 /** 140 * {@inheritDoc} 141 * 142 * @deprecated (4.2) use {@link #close()} 143 */ 144 @Override 145 @Deprecated 146 public void finish() throws IOException { 147 close(); 148 } 149 150 @Override 151 public long getContentLength() { 152 return file.length(); 153 } 154 155 @Override 156 public boolean isRepeatable() { 157 return true; 158 } 159 160 @Override 161 public void produceContent(final ContentEncoder encoder, final IOControl ioctrl) 162 throws IOException { 163 if (accessfile == null) { 164 accessfile = new RandomAccessFile(this.file, "r"); 165 } 166 if (fileChannel == null) { 167 fileChannel = accessfile.getChannel(); 168 idx = 0; 169 } 170 171 final long transferred; 172 if (useFileChannels && encoder instanceof FileContentEncoder) { 173 transferred = ((FileContentEncoder)encoder) 174 .transfer(fileChannel, idx, Long.MAX_VALUE); 175 } else { 176 transferred = fileChannel. 177 transferTo(idx, Long.MAX_VALUE, new ContentEncoderChannel(encoder)); 178 } 179 if (transferred > 0) { 180 idx += transferred; 181 } 182 if (idx >= fileChannel.size()) { 183 encoder.complete(); 184 close(); 185 } 186 } 187 188 @Override 189 public boolean isStreaming() { 190 return false; 191 } 192 193 @Override 194 public InputStream getContent() throws IOException { 195 return new FileInputStream(this.file); 196 } 197 198 @Override 199 public void writeTo(final OutputStream outstream) throws IOException { 200 Args.notNull(outstream, "Output stream"); 201 final InputStream instream = new FileInputStream(this.file); 202 try { 203 final byte[] tmp = new byte[4096]; 204 int l; 205 while ((l = instream.read(tmp)) != -1) { 206 outstream.write(tmp, 0, l); 207 } 208 outstream.flush(); 209 } finally { 210 instream.close(); 211 } 212 } 213 214}