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.annotation.NotThreadSafe;
39  import org.apache.http.entity.AbstractHttpEntity;
40  import org.apache.http.entity.ContentType;
41  import org.apache.http.nio.ContentEncoder;
42  import org.apache.http.nio.ContentEncoderChannel;
43  import org.apache.http.nio.FileContentEncoder;
44  import org.apache.http.nio.IOControl;
45  import org.apache.http.util.Args;
46  
47  /**
48   * A self contained, repeatable non-blocking entity that retrieves its content
49   * from a file. This class is mostly used to stream large files of different
50   * types, so one needs to supply the content type of the file to make sure
51   * the content can be correctly recognized and processed by the recipient.
52   *
53   * @since 4.0
54   */
55  @SuppressWarnings("deprecation")
56  @NotThreadSafe
57  public class NFileEntity extends AbstractHttpEntity
58                           implements HttpAsyncContentProducer, ProducingNHttpEntity {
59  
60      private final File file;
61      private RandomAccessFile accessfile;
62      private FileChannel fileChannel;
63      private long idx = -1;
64      private boolean useFileChannels;
65  
66      /**
67       * Creates new instance of NFileEntity from the given source {@link File}
68       * with the given content type. If <code>useFileChannels</code> is set to
69       * <code>true</code>, the entity will try to use {@link FileContentEncoder}
70       * interface to stream file content directly from the file channel.
71       *
72       * @param file the source file.
73       * @param contentType the content type of the file.
74       * @param useFileChannels flag whether the direct transfer from the file
75       *   channel should be attempted.
76       *
77       * @since 4.2
78       */
79      public NFileEntity(final File file, final ContentType contentType, final boolean useFileChannels) {
80          Args.notNull(file, "File");
81          this.file = file;
82          this.useFileChannels = useFileChannels;
83          if (contentType != null) {
84              setContentType(contentType.toString());
85          }
86      }
87  
88      /**
89       * @since 4.2
90       */
91      public NFileEntity(final File file) {
92          Args.notNull(file, "File");
93          this.file = file;
94      }
95      /**
96       * Creates new instance of NFileEntity from the given source {@link File}
97       * with the given content type.
98       *
99       * @param file the source file.
100      * @param contentType the content type of the file.
101      *
102      * @since 4.2
103      */
104     public NFileEntity(final File file, final ContentType contentType) {
105         this(file, contentType, true);
106     }
107 
108     /**
109      * @deprecated (4.2) use {@link #NFileEntity(File, ContentType, boolean)}
110      */
111     @Deprecated
112     public NFileEntity(final File file, final String contentType, final boolean useFileChannels) {
113         Args.notNull(file, "File");
114         this.file = file;
115         this.useFileChannels = useFileChannels;
116         setContentType(contentType);
117     }
118 
119     /**
120      * @deprecated (4.2) use {@link #NFileEntity(File, ContentType)}
121      */
122     @Deprecated
123     public NFileEntity(final File file, final String contentType) {
124         this(file, contentType, true);
125     }
126 
127     /**
128      * {@inheritDoc}
129      *
130      * @since 4.2
131      */
132     public void close() throws IOException {
133         if (accessfile != null) {
134             accessfile.close();
135         }
136         accessfile = null;
137         fileChannel = null;
138     }
139 
140     /**
141      * {@inheritDoc}
142      *
143      * @deprecated (4.2) use {@link #close()}
144      */
145     @Deprecated
146     public void finish() throws IOException {
147         close();
148     }
149 
150     public long getContentLength() {
151         return file.length();
152     }
153 
154     public boolean isRepeatable() {
155         return true;
156     }
157 
158     public void produceContent(final ContentEncoder encoder, final IOControl ioctrl)
159             throws IOException {
160         if (accessfile == null) {
161             accessfile = new RandomAccessFile(this.file, "r");
162         }
163         if (fileChannel == null) {
164             fileChannel = accessfile.getChannel();
165             idx = 0;
166         }
167 
168         final long transferred;
169         if (useFileChannels && encoder instanceof FileContentEncoder) {
170             transferred = ((FileContentEncoder)encoder)
171                 .transfer(fileChannel, idx, Long.MAX_VALUE);
172         } else {
173             transferred = fileChannel.
174                 transferTo(idx, Long.MAX_VALUE, new ContentEncoderChannel(encoder));
175         }
176         if (transferred > 0) {
177             idx += transferred;
178         }
179         if (idx >= fileChannel.size()) {
180             encoder.complete();
181             close();
182         }
183     }
184 
185     public boolean isStreaming() {
186         return false;
187     }
188 
189     public InputStream getContent() throws IOException {
190         return new FileInputStream(this.file);
191     }
192 
193     public void writeTo(final OutputStream outstream) throws IOException {
194         Args.notNull(outstream, "Output stream");
195         final InputStream instream = new FileInputStream(this.file);
196         try {
197             final byte[] tmp = new byte[4096];
198             int l;
199             while ((l = instream.read(tmp)) != -1) {
200                 outstream.write(tmp, 0, l);
201             }
202             outstream.flush();
203         } finally {
204             instream.close();
205         }
206     }
207 
208 }