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     @Deprecated
145     public void finish() throws IOException {
146         close();
147     }
148 
149     @Override
150     public long getContentLength() {
151         return file.length();
152     }
153 
154     @Override
155     public boolean isRepeatable() {
156         return true;
157     }
158 
159     @Override
160     public void produceContent(final ContentEncoder encoder, final IOControl ioctrl)
161             throws IOException {
162         if (accessfile == null) {
163             accessfile = new RandomAccessFile(this.file, "r");
164         }
165         if (fileChannel == null) {
166             fileChannel = accessfile.getChannel();
167             idx = 0;
168         }
169 
170         final long transferred;
171         if (useFileChannels && encoder instanceof FileContentEncoder) {
172             transferred = ((FileContentEncoder)encoder)
173                 .transfer(fileChannel, idx, Long.MAX_VALUE);
174         } else {
175             transferred = fileChannel.
176                 transferTo(idx, Long.MAX_VALUE, new ContentEncoderChannel(encoder));
177         }
178         if (transferred > 0) {
179             idx += transferred;
180         }
181         if (idx >= fileChannel.size()) {
182             encoder.complete();
183             close();
184         }
185     }
186 
187     @Override
188     public boolean isStreaming() {
189         return false;
190     }
191 
192     @Override
193     public InputStream getContent() throws IOException {
194         return new FileInputStream(this.file);
195     }
196 
197     @Override
198     public void writeTo(final OutputStream outstream) throws IOException {
199         Args.notNull(outstream, "Output stream");
200         final InputStream instream = new FileInputStream(this.file);
201         try {
202             final byte[] tmp = new byte[4096];
203             int l;
204             while ((l = instream.read(tmp)) != -1) {
205                 outstream.write(tmp, 0, l);
206             }
207             outstream.flush();
208         } finally {
209             instream.close();
210         }
211     }
212 
213 }