View Javadoc
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.nio.entity;
29  
30  import java.io.File;
31  import java.io.FileInputStream;
32  import java.io.IOException;
33  import java.io.InputStream;
34  import java.io.OutputStream;
35  import java.io.RandomAccessFile;
36  import java.nio.channels.FileChannel;
37  
38  import org.apache.http.entity.AbstractHttpEntity;
39  import org.apache.http.entity.ContentType;
40  import org.apache.http.nio.ContentEncoder;
41  import org.apache.http.nio.ContentEncoderChannel;
42  import org.apache.http.nio.FileContentEncoder;
43  import org.apache.http.nio.IOControl;
44  import org.apache.http.util.Args;
45  
46  /**
47   * A self contained, repeatable non-blocking entity that retrieves its content
48   * from a file. This class is mostly used to stream large files of different
49   * types, so one needs to supply the content type of the file to make sure
50   * the content can be correctly recognized and processed by the recipient.
51   *
52   * @since 4.0
53   */
54  @SuppressWarnings("deprecation")
55  public class NFileEntity extends AbstractHttpEntity
56                           implements HttpAsyncContentProducer, ProducingNHttpEntity {
57  
58      private final File file;
59      private RandomAccessFile accessfile;
60      private FileChannel fileChannel;
61      private long idx = -1;
62      private boolean useFileChannels;
63  
64      /**
65       * Creates new instance of NFileEntity from the given source {@link File}
66       * with the given content type. If {@code useFileChannels} is set to
67       * {@code true}, the entity will try to use {@link FileContentEncoder}
68       * interface to stream file content directly from the file channel.
69       *
70       * @param file the source file.
71       * @param contentType the content type of the file.
72       * @param useFileChannels flag whether the direct transfer from the file
73       *   channel should be attempted.
74       *
75       * @since 4.2
76       */
77      public NFileEntity(final File file, final ContentType contentType, final boolean useFileChannels) {
78          Args.notNull(file, "File");
79          this.file = file;
80          this.useFileChannels = useFileChannels;
81          if (contentType != null) {
82              setContentType(contentType.toString());
83          }
84      }
85  
86      /**
87       * @since 4.2
88       */
89      public NFileEntity(final File file) {
90          Args.notNull(file, "File");
91          this.file = file;
92      }
93      /**
94       * Creates new instance of NFileEntity from the given source {@link File}
95       * with the given content type.
96       *
97       * @param file the source file.
98       * @param contentType the content type of the file.
99       *
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 ioControl)
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 }