pythonでローカルwebサーバーをたてていい感じにファイルを管理する.
webサーバーを立ち上げてブラウザからファイルを見れるようにすることで
wifi経由でスマホやiPad等からPC内のファイルを見れるようにできる.
pythonでローカルwebサーバを立ち上げるより込み入った内容になっている.
環境
$ uname -a
Linux ubuntu20 5.4.0-73-generic
$ python --version
Python 3.8.5
簡易的に行う.
rootにしたいディレクトリで以下のコマンドを打てばいい.
$ python -m http.server 8000
http://localhost:8000/
にアクセスすると成功.
実行してるパソコン以外,スマホやiPadからならPCのIPアドレスしらべて
そのIPアドレスにアクセスすればいい.
たとえばPCのIPアドレスが192.168.11.11なら
http://192.168.11.11:8000/
にアクセスすればいい.
IPアドレスはipconfigで調べればいい.
カスタマイズする.
なんか物足りないので自分でカスタマイズする.
クライアント(ブラウザ)側でjavascript書くなりする方法と
サーバー側でpythonいじる方法があるが
複数の端末で使用することを想定するとサーバーを書き換えたほうが良さそう.
http.serverの公式ドキュメントやソースとにらめっこすると
SimpleHTTPRequestHandlerのlist_directoryがページ生成してることがわかるので
継承して目的の関数だけオーバーロードする.
- 隠しフォルダを表示させない
- 画像やpdfファイルのサムネイルを表示させる.
なんかいい感じになった.
ただしpdfのサムネは別途つくる必要あり.
もとのソースの変更を最小限にして書き換えてみた.推奨されていない表記もあるが
もとのソースに準じてそのままにしている.
# import http.server
from http.server import test, SimpleHTTPRequestHandler, CGIHTTPRequestHandler, ThreadingHTTPServer
import html
import io
import os
import socket # For gethostbyaddr()
import sys
import urllib.parse
import contextlib
from functools import partial
from http import HTTPStatus
class MyHTTPRequestHandler(SimpleHTTPRequestHandler):
def list_directory(self, path):
"""Helper to produce a directory listing (absent index.html).
Return value is either a file object, or None (indicating an
error). In either case, the headers are sent, making the
interface the same as for send_head().
"""
try:
list = os.listdir(path)
except OSError:
self.send_error(
HTTPStatus.NOT_FOUND,
"No permission to list directory")
return None
list.sort(key=lambda a: a.lower())
r = []
try:
displaypath = urllib.parse.unquote(self.path,
errors='surrogatepass')
except UnicodeDecodeError:
displaypath = urllib.parse.unquote(path)
displaypath = html.escape(displaypath, quote=False)
enc = sys.getfilesystemencoding()
title = 'Directory listing for %s' % displaypath
r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
'"http://www.w3.org/TR/html4/strict.dtd">')
r.append('<html>\n<head>')
r.append('<meta http-equiv="Content-Type" '
'content="text/html; charset=%s">' % enc)
r.append('<title>%s</title>\n</head>' % title)
# cssの追加
r.append('<style>img{height:240px}</style>\n')
r.append('<body>\n<h1>%s</h1>' % title)
r.append('<hr>\n<ul>')
r.append('<div style="float:left">')
r_sub = []
for name in list:
# 隠しフォルダを表示しない.
# linux以外では知らん.
if name[0] == '.':
continue
#
fullname = os.path.join(path, name)
displayname = linkname = name
# Append / for directories or @ for symbolic links
if os.path.isdir(fullname):
displayname = name + "/"
linkname = name + "/"
if os.path.islink(fullname):
displayname = name + "@"
# Note: a link to a directory displays with @ and links with /
r.append('<li><a href="%s">%s</a></li>'
% (urllib.parse.quote(linkname,
errors='surrogatepass'),
html.escape(displayname, quote=False)))
# ---- My Settings ----
# 画像とPDFの場合,サムネも表示させるようにする.
# PDFのサムネは事前に作成して,規定の場所に保存しておく.
base, extension = os.path.splitext(name)
if extension in [".png", ".jpg", ".jpeg"]:
quote_path = urllib.parse.quote(
linkname, errors='surrogatepass')
r_sub.append('<a href="%s"><img src="%s"></img></a>'
% (quote_path, quote_path))
elif extension in [".pdf"]:
quote_path = urllib.parse.quote(
linkname, errors='surrogatepass')
head, tail = os.path.split(quote_path)
thumnail_path = os.path.join(head, ".thumnail", base + ".jpeg")
r_sub.append('<a href="%s"><img src="%s"></img></a>' %
(quote_path, thumnail_path))
r.append('</div>')
r += r_sub
r.append('<div style="clear:left"></div>')
# ---- My Setting ----
r.append('</ul>\n<hr>\n</body>\n</html>\n')
encoded = '\n'.join(r).encode(enc, 'surrogateescape')
f = io.BytesIO()
f.write(encoded)
f.seek(0)
self.send_response(HTTPStatus.OK)
self.send_header("Content-type", "text/html; charset=%s" % enc)
self.send_header("Content-Length", str(len(encoded)))
self.end_headers()
return f
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--cgi', action='store_true',
help='Run as CGI Server')
parser.add_argument('--bind', '-b', metavar='ADDRESS',
help='Specify alternate bind address '
'[default: all interfaces]')
parser.add_argument('--directory', '-d', default=os.getcwd(),
help='Specify alternative directory '
'[default:current directory]')
parser.add_argument('port', action='store',
default=8000, type=int,
nargs='?',
help='Specify alternate port [default: 8000]')
args = parser.parse_args()
if args.cgi:
handler_class = CGIHTTPRequestHandler
else:
# ここだけSimpleHTTPRequestHandlerからMyHTTPRequestHandlerに書き換える
handler_class = partial(MyHTTPRequestHandler,
directory=args.directory)
# ensure dual-stack is not disabled; ref #38907
class DualStackServer(ThreadingHTTPServer):
def server_bind(self):
# suppress exception when protocol is IPv4
with contextlib.suppress(Exception):
self.socket.setsockopt(
socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
return super().server_bind()
test(
HandlerClass=handler_class,
ServerClass=DualStackServer,
port=args.port,
bind=args.bind,
)
あとは
python myserver.py
で起動する.
これでiPadからPCに保存した自炊本たちが見れる〜〜.
pythonつかってpdfからサムネ作成するコードは需要があれば別途あげる.