24

I have tried reading the docs for Bottle, however, I am still unsure about how static file serving works. I have an index.tpl file, and within it it has a css file attached to it, and it works. However, I was reading that Bottle does not automatically serve css files, which can't be true if the page loads correctly.

I have, however, run into speed issues when requesting the page. Is that because I didn't use the return static_file(params go here)? If someone could clear up how they work, and how they are used when loading the page, it would be great.

Server code:

from Bottle import route,run,template,request,static_file



@route('/')
def home():
    return template('Templates/index',name=request.environ.get('REMOTE_ADDR'))

run(host='Work-PC',port=9999,debug=True)

Index:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta http-equiv="content-type"
 content="text/html; charset=ISO-8859-1">
  <title>index</title>
  <link type="text/css"
 href="cssfiles/mainpagecss.css"
 rel="stylesheet">
</head>
<body>
<table
 style="width: 100%; text-align: left; margin-left: auto; margin-right: auto;"
 border="0" cellpadding="2" cellspacing="2">
  <tbody>
    <tr>
      <td>
      <h1><span class="headertext">
      <center>Network
Website</center>
      </span></h1>
      </td>
    </tr>
  </tbody>
</table>
%if name!='none':
    <p align="right">signed in as: {{name}}</p>
%else:
    pass
%end
<br>
<table style="text-align: left; width: 100%;" border="0" cellpadding="2"
 cellspacing="2">
  <tbody>
    <tr>
      <td>
      <table style="text-align: left; width: 100%;" border="0"
 cellpadding="2" cellspacing="2">
        <tbody>
          <tr>
            <td style="width: 15%; vertical-align: top;">
            <table style="text-align: left; width: 100%;" border="1"
 cellpadding="2" cellspacing="2">
              <tbody>
                <tr>
                  <td>Home<br>
                  <span class="important">Teamspeak Download</span><br>
                  <span class="important">Teamspeak Information</span></td>
                </tr>
              </tbody>
            </table>
            </td>
            <td style="vertical-align: top;">
            <table style="text-align: left; width: 100%;" border="1"
 cellpadding="2" cellspacing="2">
              <tbody>
                <tr>
                  <td>
                  <h1><span style="font-weight: bold;">Network Website</span></h1>
To find all of the needed information relating to the network's social
capabilities, please refer to the links in the side bar.</td>
                </tr>
              </tbody>
            </table>
            </td>
          </tr>
        </tbody>
      </table>
      </td>
    </tr>
  </tbody>
</table>
</body>
</html>
IT Ninja
  • 5,564
  • 9
  • 37
  • 63
  • 1
    I don't know much about bottle, but the statement saying that it doesn't serve static files might apply only to production mode, not development. It's usually much more effective to let your Apache or whatever you're using serve static files. And by the way... you have waaaaay too many tables in your HTML ;) – pcalcao May 07 '12 at 17:18

5 Answers5

73

To serve static files using bottle you'll need to use the provided static_file function and add a few additional routes. The following routes direct the static file requests and ensure that only files with the correct file extension are accessed.

from bottle import get, static_file

# Static Routes
@get("/static/css/<filepath:re:.*\.css>")
def css(filepath):
    return static_file(filepath, root="static/css")

@get("/static/font/<filepath:re:.*\.(eot|otf|svg|ttf|woff|woff2?)>")
def font(filepath):
    return static_file(filepath, root="static/font")

@get("/static/img/<filepath:re:.*\.(jpg|png|gif|ico|svg)>")
def img(filepath):
    return static_file(filepath, root="static/img")

@get("/static/js/<filepath:re:.*\.js>")
def js(filepath):
    return static_file(filepath, root="static/js")

Now in your html, you can reference a file like so:

<link type="text/css" href="/static/css/main.css" rel="stylesheet">

Directory layout:

`--static
|  `--css
|  `--fonts
|  `--img
|  `--js
Sanketh Katta
  • 5,181
  • 2
  • 24
  • 30
  • What's the name of your module that contains `# Static Routes`, and where is it located in your project's directory? – Droogans Nov 06 '12 at 20:49
  • 1
    I have a `server.py` file in the root of my project directory that runs the server and contains the static routes, and the static directory is simply in the root of the project directory as well. – Sanketh Katta Nov 06 '12 at 21:04
  • 4
    use `<:re:.>`so that `some/path/some.js` and `some.js` works – peko Jun 23 '15 at 21:27
  • 1
    @SankethKatta there is a recurring type, extra closing bracket: return static_file(filepath, root="static/img") here>>)< – Natalie Perret May 13 '18 at 01:43
14

Just providing an answer here because a number of my students have used this code in an assignment and I have a bit of a concern about the solution.

The standard way to serve static files in Bottle is in the documentation:

from bottle import static_file
@route('/static/<filepath:path>')
def server_static(filepath):
    return static_file(filepath, root='/path/to/your/static/files')

in this way, all of the files below your static folder are served from a URL starting with /static. In your HTML, you need to reference the full URL path for the resource, eg:

<link rel='stylesheet' type='text/css' href='/static/css/style.css'>

The answer from Sanketh makes it so that any reference to an image, css file etc anywhere in the URL space is served from a given folder inside the static folder. So /foo/bar/baz/picture.jpg and /picture.jpg would both be served from static/images/picture.jpg. This means that you don't need to worry about getting the path right in your HTML code and you can always use relative filenames (ie. just src="picture.jpg").

The problem with this approach comes when you try to deploy your application. In a production environment you want the static resources to be served by a web server like nginx, not by your Bottle application. To enable this, they should all be served from a single part of the URL space, eg. /static. If your code is littered with relative filenames, it won't translate easily to this model.

So, I'd advise using the three line solution from the Bottle tutorial rather than the more complex solution listed on this page. It's simpler code (so less likely to be buggy) and it allows you to seamlessly move to a production environment without code changes.

Steve Cassidy
  • 195
  • 1
  • 7
  • 1
    I wrote my original answer a number of years back and my project at the time made it nicer to reference files the way I did for my use case. I've updated the answer to be more in line with the tutorial and play nice with a production deployment. However, I left in the separated routes per file type to keep an extra check on the allowed file extensions in each folder. – Sanketh Katta Dec 08 '16 at 02:38
4

As indicated in the documentation, you should serve static files using the static function and css is a static file. The static function handles security and some other function which you can find out from the source. The path argument to the static function should point to the directory wherever you store the css files

cobie
  • 5,869
  • 8
  • 34
  • 58
  • so how should i set it up to work with my template? Or is this just when deploying to a production server, not a dev. server? – IT Ninja May 07 '12 at 18:49
  • Bottle has a variable called **_TEMPLATE_PATH_** which is a list of paths which Bottle searches for templates. Check line 2839 in the bottle.py source file. You either use one of the default paths or you can add the path which you are using to that list. – cobie May 07 '12 at 18:55
  • So if i add my template path to that variable, then use `return static_file(pathtomycss)` thats how i should do it? If not, could you give me an example? Im sorry about all these questions, im just starting off in web developing x.x – IT Ninja May 07 '12 at 19:02
  • No..you can add the path to your _html template_ files to the template path but the css files are normally included in a different folder. You pass the path to that folder to the static_file function. – cobie May 07 '12 at 19:06
  • so I have two return functions? template and static file? – IT Ninja May 07 '12 at 20:14
3

Rather than use regular expression matching for serving files as in Sanketh's answer, I'd prefer not to modify my templates and provide a path to the static files explicitly, as in:

<script src="{{ get_url('static', filename='js/bootstrap.min.js') }}"></script>

You can do this simply by replacing the <filename> in the static route decorator with one of type :path - like so:

@app.route('/static/<filename:path>', name='static')
def serve_static(filename):
    return static_file(filename, root=config.STATIC_PATH)

The :path matches an entire file path in a non-greedy way so you don't have to worry about changing templates when switching to production - just keep everything in the same relative folder structure.

Aaron D
  • 6,424
  • 3
  • 38
  • 45
  • **Note:** In this example I am using `get_url` in my template, but you could just as easily specify the link manually - `/static/js/bootstrap.min.js`. – Aaron D Jul 28 '15 at 03:06
2

I've used Sanketh's template in the past but over time condensed it to an extension agnostic function. You just have to add extension-folder mappings in the ext_map dictionary. It defaults to static/ folder if an extension is not mapped explicitly.

import os.path    

# Static Routes
@get('/<filename>')
def serve_static_file(filename):
    ext = os.path.splitext(filename)[1][1:]
    ext_map = {'image':['png','gif','jpg','ico'],'js':['js']}
    sub_folder = next((k for k, v in ext_map.items() if ext in v),'')
    return static_file(filename, root='static/'+sub_folder)
phoxley
  • 454
  • 5
  • 18