Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/studio/static/doc/flask-docs/patterns/fileuploads.html
blob: 2941367b71ab95105b99098a50a7b0269993151a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    
    <title>Uploading Files &mdash; Flask 0.8 documentation</title>
    
    <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
    <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
    
    <script type="text/javascript">
      var DOCUMENTATION_OPTIONS = {
        URL_ROOT:    '../',
        VERSION:     '0.8',
        COLLAPSE_INDEX: false,
        FILE_SUFFIX: '.html',
        HAS_SOURCE:  true
      };
    </script>
    <script type="text/javascript" src="../_static/jquery.js"></script>
    <script type="text/javascript" src="../_static/underscore.js"></script>
    <script type="text/javascript" src="../_static/doctools.js"></script>
    <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
    <link rel="up" title="Patterns for Flask" href="index.html" />
    <link rel="next" title="Caching" href="caching.html" />
    <link rel="prev" title="SQLAlchemy in Flask" href="sqlalchemy.html" />
   
  
  <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
  
  <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />

  </head>
  <body>
    <div class="related">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="../genindex.html" title="General Index"
             accesskey="I">index</a></li>
        <li class="right" >
          <a href="caching.html" title="Caching"
             accesskey="N">next</a> |</li>
        <li class="right" >
          <a href="sqlalchemy.html" title="SQLAlchemy in Flask"
             accesskey="P">previous</a> |</li>
        <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
          <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li> 
      </ul>
    </div>  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body">
            
  <div class="section" id="uploading-files">
<span id="id1"></span><h1>Uploading Files<a class="headerlink" href="#uploading-files" title="Permalink to this headline">¶</a></h1>
<p>Ah yes, the good old problem of file uploads.  The basic idea of file
uploads is actually quite simple.  It basically works like this:</p>
<ol class="arabic simple">
<li>A <tt class="docutils literal"><span class="pre">&lt;form&gt;</span></tt> tag is marked with <tt class="docutils literal"><span class="pre">enctype=multipart/form-data</span></tt>
and an <tt class="docutils literal"><span class="pre">&lt;input</span> <span class="pre">type=file&gt;</span></tt> is placed in that form.</li>
<li>The application accesses the file from the <tt class="xref py py-attr docutils literal"><span class="pre">files</span></tt>
dictionary on the request object.</li>
<li>use the <a class="reference external" href="http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.FileStorage.save" title="(in Werkzeug v0.7)"><tt class="xref py py-meth docutils literal"><span class="pre">save()</span></tt></a> method of the file to save
the file permanently somewhere on the filesystem.</li>
</ol>
<div class="section" id="a-gentle-introduction">
<h2>A Gentle Introduction<a class="headerlink" href="#a-gentle-introduction" title="Permalink to this headline">¶</a></h2>
<p>Let&#8217;s start with a very basic application that uploads a file to a
specific upload folder and displays a file to the user.  Let&#8217;s look at the
bootstrapping code for our application:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">redirect</span><span class="p">,</span> <span class="n">url_for</span>
<span class="kn">from</span> <span class="nn">werkzeug</span> <span class="kn">import</span> <span class="n">secure_filename</span>

<span class="n">UPLOAD_FOLDER</span> <span class="o">=</span> <span class="s">&#39;/path/to/the/uploads&#39;</span>
<span class="n">ALLOWED_EXTENSIONS</span> <span class="o">=</span> <span class="nb">set</span><span class="p">([</span><span class="s">&#39;txt&#39;</span><span class="p">,</span> <span class="s">&#39;pdf&#39;</span><span class="p">,</span> <span class="s">&#39;png&#39;</span><span class="p">,</span> <span class="s">&#39;jpg&#39;</span><span class="p">,</span> <span class="s">&#39;jpeg&#39;</span><span class="p">,</span> <span class="s">&#39;gif&#39;</span><span class="p">])</span>

<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;UPLOAD_FOLDER&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">UPLOAD_FOLDER</span>
</pre></div>
</div>
<p>So first we need a couple of imports.  Most should be straightforward, the
<tt class="xref py py-func docutils literal"><span class="pre">werkzeug.secure_filename()</span></tt> is explained a little bit later.  The
<cite>UPLOAD_FOLDER</cite> is where we will store the uploaded files and the
<cite>ALLOWED_EXTENSIONS</cite> is the set of allowed file extensions.  Then we add a
URL rule by hand to the application.  Now usually we&#8217;re not doing that, so
why here?  The reasons is that we want the webserver (or our development
server) to serve these files for us and so we only need a rule to generate
the URL to these files.</p>
<p>Why do we limit the extensions that are allowed?  You probably don&#8217;t want
your users to be able to upload everything there if the server is directly
sending out the data to the client.  That way you can make sure that users
are not able to upload HTML files that would cause XSS problems (see
<a class="reference internal" href="../security.html#xss"><em>Cross-Site Scripting (XSS)</em></a>).  Also make sure to disallow <cite>.php</cite> files if the server
executes them, but who has PHP installed on his server, right?  :)</p>
<p>Next the functions that check if an extension is valid and that uploads
the file and redirects the user to the URL for the uploaded file:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">allowed_file</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
    <span class="k">return</span> <span class="s">&#39;.&#39;</span> <span class="ow">in</span> <span class="n">filename</span> <span class="ow">and</span> \
           <span class="n">filename</span><span class="o">.</span><span class="n">rsplit</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span> <span class="ow">in</span> <span class="n">ALLOWED_EXTENSIONS</span>

<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;POST&#39;</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">upload_file</span><span class="p">():</span>
    <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span><span class="p">:</span>
        <span class="nb">file</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">files</span><span class="p">[</span><span class="s">&#39;file&#39;</span><span class="p">]</span>
        <span class="k">if</span> <span class="nb">file</span> <span class="ow">and</span> <span class="n">allowed_file</span><span class="p">(</span><span class="nb">file</span><span class="o">.</span><span class="n">filename</span><span class="p">):</span>
            <span class="n">filename</span> <span class="o">=</span> <span class="n">secure_filename</span><span class="p">(</span><span class="nb">file</span><span class="o">.</span><span class="n">filename</span><span class="p">)</span>
            <span class="nb">file</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;UPLOAD_FOLDER&#39;</span><span class="p">],</span> <span class="n">filename</span><span class="p">))</span>
            <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;uploaded_file&#39;</span><span class="p">,</span>
                                    <span class="n">filename</span><span class="o">=</span><span class="n">filename</span><span class="p">))</span>
    <span class="k">return</span> <span class="s">&#39;&#39;&#39;</span>
<span class="s">    &lt;!doctype html&gt;</span>
<span class="s">    &lt;title&gt;Upload new File&lt;/title&gt;</span>
<span class="s">    &lt;h1&gt;Upload new File&lt;/h1&gt;</span>
<span class="s">    &lt;form action=&quot;&quot; method=post enctype=multipart/form-data&gt;</span>
<span class="s">      &lt;p&gt;&lt;input type=file name=file&gt;</span>
<span class="s">         &lt;input type=submit value=Upload&gt;</span>
<span class="s">    &lt;/form&gt;</span>
<span class="s">    &#39;&#39;&#39;</span>
</pre></div>
</div>
<p>So what does that <a class="reference external" href="http://werkzeug.pocoo.org/docs/utils/#werkzeug.utils.secure_filename" title="(in Werkzeug v0.7)"><tt class="xref py py-func docutils literal"><span class="pre">secure_filename()</span></tt></a> function actually do?
Now the problem is that there is that principle called &#8220;never trust user
input&#8221;.  This is also true for the filename of an uploaded file.  All
submitted form data can be forged, and filenames can be dangerous.  For
the moment just remember: always use that function to secure a filename
before storing it directly on the filesystem.</p>
<div class="admonition-information-for-the-pros admonition ">
<p class="first admonition-title">Information for the Pros</p>
<p>So you&#8217;re interested in what that <a class="reference external" href="http://werkzeug.pocoo.org/docs/utils/#werkzeug.utils.secure_filename" title="(in Werkzeug v0.7)"><tt class="xref py py-func docutils literal"><span class="pre">secure_filename()</span></tt></a>
function does and what the problem is if you&#8217;re not using it?  So just
imagine someone would send the following information as <cite>filename</cite> to
your application:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">filename</span> <span class="o">=</span> <span class="s">&quot;../../../../home/username/.bashrc&quot;</span>
</pre></div>
</div>
<p>Assuming the number of <tt class="docutils literal"><span class="pre">../</span></tt> is correct and you would join this with
the <cite>UPLOAD_FOLDER</cite> the user might have the ability to modify a file on
the server&#8217;s filesystem he or she should not modify.  This does require some
knowledge about how the application looks like, but trust me, hackers
are patient :)</p>
<p>Now let&#8217;s look how that function works:</p>
<div class="last highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">secure_filename</span><span class="p">(</span><span class="s">&#39;../../../../home/username/.bashrc&#39;</span><span class="p">)</span>
<span class="go">&#39;home_username_.bashrc&#39;</span>
</pre></div>
</div>
</div>
<p>Now one last thing is missing: the serving of the uploaded files.  As of
Flask 0.5 we can use a function that does that for us:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">send_from_directory</span>

<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/uploads/&lt;filename&gt;&#39;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">uploaded_file</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">send_from_directory</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;UPLOAD_FOLDER&#39;</span><span class="p">],</span>
                               <span class="n">filename</span><span class="p">)</span>
</pre></div>
</div>
<p>Alternatively you can register <cite>uploaded_file</cite> as <cite>build_only</cite> rule and
use the <a class="reference external" href="http://werkzeug.pocoo.org/docs/middlewares/#werkzeug.wsgi.SharedDataMiddleware" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">SharedDataMiddleware</span></tt></a>.  This also works with
older versions of Flask:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">werkzeug</span> <span class="kn">import</span> <span class="n">SharedDataMiddleware</span>
<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/uploads/&lt;filename&gt;&#39;</span><span class="p">,</span> <span class="s">&#39;uploaded_file&#39;</span><span class="p">,</span>
                 <span class="n">build_only</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">app</span><span class="o">.</span><span class="n">wsgi_app</span> <span class="o">=</span> <span class="n">SharedDataMiddleware</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">wsgi_app</span><span class="p">,</span> <span class="p">{</span>
    <span class="s">&#39;/uploads&#39;</span><span class="p">:</span>  <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;UPLOAD_FOLDER&#39;</span><span class="p">]</span>
<span class="p">})</span>
</pre></div>
</div>
<p>If you now run the application everything should work as expected.</p>
</div>
<div class="section" id="improving-uploads">
<h2>Improving Uploads<a class="headerlink" href="#improving-uploads" title="Permalink to this headline">¶</a></h2>
<p class="versionadded">
<span class="versionmodified">New in version 0.6.</span></p>
<p>So how exactly does Flask handle uploads?  Well it will store them in the
webserver&#8217;s memory if the files are reasonable small otherwise in a
temporary location (as returned by <a class="reference external" href="http://docs.python.org/dev/library/tempfile.html#tempfile.gettempdir" title="(in Python v3.3)"><tt class="xref py py-func docutils literal"><span class="pre">tempfile.gettempdir()</span></tt></a>).  But how
do you specify the maximum file size after which an upload is aborted?  By
default Flask will happily accept file uploads to an unlimited amount of
memory, but you can limit that by setting the <tt class="docutils literal"><span class="pre">MAX_CONTENT_LENGTH</span></tt>
config key:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">Request</span>

<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;MAX_CONTENT_LENGTH&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="mi">16</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span>
</pre></div>
</div>
<p>The code above will limited the maximum allowed payload to 16 megabytes.
If a larger file is transmitted, Flask will raise an
<a class="reference external" href="http://werkzeug.pocoo.org/docs/exceptions/#werkzeug.exceptions.RequestEntityTooLarge" title="(in Werkzeug v0.7)"><tt class="xref py py-exc docutils literal"><span class="pre">RequestEntityTooLarge</span></tt></a> exception.</p>
<p>This feature was added in Flask 0.6 but can be achieved in older versions
as well by subclassing the request object.  For more information on that
consult the Werkzeug documentation on file handling.</p>
</div>
<div class="section" id="upload-progress-bars">
<h2>Upload Progress Bars<a class="headerlink" href="#upload-progress-bars" title="Permalink to this headline">¶</a></h2>
<p>A while ago many developers had the idea to read the incoming file in
small chunks and store the upload progress in the database to be able to
poll the progress with JavaScript from the client.  Long story short: the
client asks the server every 5 seconds how much it has transmitted
already.  Do you realize the irony?  The client is asking for something it
should already know.</p>
<p>Now there are better solutions to that work faster and more reliable.  The
web changed a lot lately and you can use HTML5, Java, Silverlight or Flash
to get a nicer uploading experience on the client side.  Look at the
following libraries for some nice examples how to do that:</p>
<ul class="simple">
<li><a class="reference external" href="http://www.plupload.com/">Plupload</a> - HTML5, Java, Flash</li>
<li><a class="reference external" href="http://www.swfupload.org/">SWFUpload</a> - Flash</li>
<li><a class="reference external" href="http://jumploader.com/">JumpLoader</a> - Java</li>
</ul>
</div>
<div class="section" id="an-easier-solution">
<h2>An Easier Solution<a class="headerlink" href="#an-easier-solution" title="Permalink to this headline">¶</a></h2>
<p>Because the common pattern for file uploads exists almost unchanged in all
applications dealing with uploads, there is a Flask extension called
<a class="reference external" href="http://packages.python.org/Flask-Uploads/">Flask-Uploads</a> that implements a full fledged upload mechanism with
white and blacklisting of extensions and more.</p>
</div>
</div>


          </div>
        </div>
      </div>
      <div class="sphinxsidebar">
        <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
  <img class="logo" src="../_static/flask.png" alt="Logo"/>
</a></p>
  <h3><a href="../index.html">Table Of Contents</a></h3>
  <ul>
<li><a class="reference internal" href="#">Uploading Files</a><ul>
<li><a class="reference internal" href="#a-gentle-introduction">A Gentle Introduction</a></li>
<li><a class="reference internal" href="#improving-uploads">Improving Uploads</a></li>
<li><a class="reference internal" href="#upload-progress-bars">Upload Progress Bars</a></li>
<li><a class="reference internal" href="#an-easier-solution">An Easier Solution</a></li>
</ul>
</li>
</ul>
<h3>Related Topics</h3>
<ul>
  <li><a href="../index.html">Documentation overview</a><ul>
  <li><a href="index.html">Patterns for Flask</a><ul>
      <li>Previous: <a href="sqlalchemy.html" title="previous chapter">SQLAlchemy in Flask</a></li>
      <li>Next: <a href="caching.html" title="next chapter">Caching</a></li>
  </ul></li>
  </ul></li>
</ul>
  <h3>This Page</h3>
  <ul class="this-page-menu">
    <li><a href="../_sources/patterns/fileuploads.txt"
           rel="nofollow">Show Source</a></li>
  </ul>
<div id="searchbox" style="display: none">
  <h3>Quick search</h3>
    <form class="search" action="../search.html" method="get">
      <input type="text" name="q" />
      <input type="submit" value="Go" />
      <input type="hidden" name="check_keywords" value="yes" />
      <input type="hidden" name="area" value="default" />
    </form>
    <p class="searchtip" style="font-size: 90%">
    Enter search terms or a module, class or function name.
    </p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
        </div>
      </div>
      <div class="clearer"></div>
    </div>
    <div class="footer">
      &copy; Copyright 2010, Armin Ronacher.
      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
    </div>
  </body>
</html>