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