Unused code was removed
This commit is contained in:
parent
f6e73d1c17
commit
0aa42552f8
14 changed files with 0 additions and 2079 deletions
|
@ -1,12 +0,0 @@
|
||||||
application: jquery-file-upload
|
|
||||||
version: 2
|
|
||||||
runtime: go
|
|
||||||
api_version: go1
|
|
||||||
|
|
||||||
handlers:
|
|
||||||
- url: /(favicon\.ico|robots\.txt)
|
|
||||||
static_files: static/\1
|
|
||||||
upload: static/(.*)
|
|
||||||
expiration: '1d'
|
|
||||||
- url: /.*
|
|
||||||
script: _go_app
|
|
|
@ -1,361 +0,0 @@
|
||||||
/*
|
|
||||||
* jQuery File Upload Plugin GAE Go Example
|
|
||||||
* https://github.com/blueimp/jQuery-File-Upload
|
|
||||||
*
|
|
||||||
* Copyright 2011, Sebastian Tschan
|
|
||||||
* https://blueimp.net
|
|
||||||
*
|
|
||||||
* Licensed under the MIT license:
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/disintegration/gift"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/appengine"
|
|
||||||
"google.golang.org/appengine/memcache"
|
|
||||||
"hash/crc32"
|
|
||||||
"image"
|
|
||||||
"image/gif"
|
|
||||||
"image/jpeg"
|
|
||||||
"image/png"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"mime/multipart"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
WEBSITE = "https://blueimp.github.io/jQuery-File-Upload/"
|
|
||||||
MIN_FILE_SIZE = 1 // bytes
|
|
||||||
// Max file size is memcache limit (1MB) minus key size minus overhead:
|
|
||||||
MAX_FILE_SIZE = 999000 // bytes
|
|
||||||
IMAGE_TYPES = "image/(gif|p?jpeg|(x-)?png)"
|
|
||||||
ACCEPT_FILE_TYPES = IMAGE_TYPES
|
|
||||||
THUMB_MAX_WIDTH = 80
|
|
||||||
THUMB_MAX_HEIGHT = 80
|
|
||||||
EXPIRATION_TIME = 300 // seconds
|
|
||||||
// If empty, only allow redirects to the referer protocol+host.
|
|
||||||
// Set to a regexp string for custom pattern matching:
|
|
||||||
REDIRECT_ALLOW_TARGET = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
imageTypes = regexp.MustCompile(IMAGE_TYPES)
|
|
||||||
acceptFileTypes = regexp.MustCompile(ACCEPT_FILE_TYPES)
|
|
||||||
thumbSuffix = "." + fmt.Sprint(THUMB_MAX_WIDTH) + "x" +
|
|
||||||
fmt.Sprint(THUMB_MAX_HEIGHT)
|
|
||||||
)
|
|
||||||
|
|
||||||
func escape(s string) string {
|
|
||||||
return strings.Replace(url.QueryEscape(s), "+", "%20", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractKey(r *http.Request) string {
|
|
||||||
// Use RequestURI instead of r.URL.Path, as we need the encoded form:
|
|
||||||
path := strings.Split(r.RequestURI, "?")[0]
|
|
||||||
// Also adjust double encoded slashes:
|
|
||||||
return strings.Replace(path[1:], "%252F", "%2F", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func check(err error) {
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type FileInfo struct {
|
|
||||||
Key string `json:"-"`
|
|
||||||
ThumbnailKey string `json:"-"`
|
|
||||||
Url string `json:"url,omitempty"`
|
|
||||||
ThumbnailUrl string `json:"thumbnailUrl,omitempty"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Size int64 `json:"size"`
|
|
||||||
Error string `json:"error,omitempty"`
|
|
||||||
DeleteUrl string `json:"deleteUrl,omitempty"`
|
|
||||||
DeleteType string `json:"deleteType,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fi *FileInfo) ValidateType() (valid bool) {
|
|
||||||
if acceptFileTypes.MatchString(fi.Type) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
fi.Error = "Filetype not allowed"
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fi *FileInfo) ValidateSize() (valid bool) {
|
|
||||||
if fi.Size < MIN_FILE_SIZE {
|
|
||||||
fi.Error = "File is too small"
|
|
||||||
} else if fi.Size > MAX_FILE_SIZE {
|
|
||||||
fi.Error = "File is too big"
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fi *FileInfo) CreateUrls(r *http.Request, c context.Context) {
|
|
||||||
u := &url.URL{
|
|
||||||
Scheme: r.URL.Scheme,
|
|
||||||
Host: appengine.DefaultVersionHostname(c),
|
|
||||||
Path: "/",
|
|
||||||
}
|
|
||||||
uString := u.String()
|
|
||||||
fi.Url = uString + fi.Key
|
|
||||||
fi.DeleteUrl = fi.Url
|
|
||||||
fi.DeleteType = "DELETE"
|
|
||||||
if fi.ThumbnailKey != "" {
|
|
||||||
fi.ThumbnailUrl = uString + fi.ThumbnailKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fi *FileInfo) SetKey(checksum uint32) {
|
|
||||||
fi.Key = escape(string(fi.Type)) + "/" +
|
|
||||||
escape(fmt.Sprint(checksum)) + "/" +
|
|
||||||
escape(string(fi.Name))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fi *FileInfo) createThumb(buffer *bytes.Buffer, c context.Context) {
|
|
||||||
if imageTypes.MatchString(fi.Type) {
|
|
||||||
src, _, err := image.Decode(bytes.NewReader(buffer.Bytes()))
|
|
||||||
check(err)
|
|
||||||
filter := gift.New(gift.ResizeToFit(
|
|
||||||
THUMB_MAX_WIDTH,
|
|
||||||
THUMB_MAX_HEIGHT,
|
|
||||||
gift.LanczosResampling,
|
|
||||||
))
|
|
||||||
dst := image.NewNRGBA(filter.Bounds(src.Bounds()))
|
|
||||||
filter.Draw(dst, src)
|
|
||||||
buffer.Reset()
|
|
||||||
bWriter := bufio.NewWriter(buffer)
|
|
||||||
switch fi.Type {
|
|
||||||
case "image/jpeg", "image/pjpeg":
|
|
||||||
err = jpeg.Encode(bWriter, dst, nil)
|
|
||||||
case "image/gif":
|
|
||||||
err = gif.Encode(bWriter, dst, nil)
|
|
||||||
default:
|
|
||||||
err = png.Encode(bWriter, dst)
|
|
||||||
}
|
|
||||||
check(err)
|
|
||||||
bWriter.Flush()
|
|
||||||
thumbnailKey := fi.Key + thumbSuffix + filepath.Ext(fi.Name)
|
|
||||||
item := &memcache.Item{
|
|
||||||
Key: thumbnailKey,
|
|
||||||
Value: buffer.Bytes(),
|
|
||||||
}
|
|
||||||
err = memcache.Set(c, item)
|
|
||||||
check(err)
|
|
||||||
fi.ThumbnailKey = thumbnailKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleUpload(r *http.Request, p *multipart.Part) (fi *FileInfo) {
|
|
||||||
fi = &FileInfo{
|
|
||||||
Name: p.FileName(),
|
|
||||||
Type: p.Header.Get("Content-Type"),
|
|
||||||
}
|
|
||||||
if !fi.ValidateType() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if rec := recover(); rec != nil {
|
|
||||||
log.Println(rec)
|
|
||||||
fi.Error = rec.(error).Error()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
hash := crc32.NewIEEE()
|
|
||||||
mw := io.MultiWriter(&buffer, hash)
|
|
||||||
lr := &io.LimitedReader{R: p, N: MAX_FILE_SIZE + 1}
|
|
||||||
_, err := io.Copy(mw, lr)
|
|
||||||
check(err)
|
|
||||||
fi.Size = MAX_FILE_SIZE + 1 - lr.N
|
|
||||||
if !fi.ValidateSize() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fi.SetKey(hash.Sum32())
|
|
||||||
item := &memcache.Item{
|
|
||||||
Key: fi.Key,
|
|
||||||
Value: buffer.Bytes(),
|
|
||||||
}
|
|
||||||
context := appengine.NewContext(r)
|
|
||||||
err = memcache.Set(context, item)
|
|
||||||
check(err)
|
|
||||||
fi.createThumb(&buffer, context)
|
|
||||||
fi.CreateUrls(r, context)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFormValue(p *multipart.Part) string {
|
|
||||||
var b bytes.Buffer
|
|
||||||
io.CopyN(&b, p, int64(1<<20)) // Copy max: 1 MiB
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleUploads(r *http.Request) (fileInfos []*FileInfo) {
|
|
||||||
fileInfos = make([]*FileInfo, 0)
|
|
||||||
mr, err := r.MultipartReader()
|
|
||||||
check(err)
|
|
||||||
r.Form, err = url.ParseQuery(r.URL.RawQuery)
|
|
||||||
check(err)
|
|
||||||
part, err := mr.NextPart()
|
|
||||||
for err == nil {
|
|
||||||
if name := part.FormName(); name != "" {
|
|
||||||
if part.FileName() != "" {
|
|
||||||
fileInfos = append(fileInfos, handleUpload(r, part))
|
|
||||||
} else {
|
|
||||||
r.Form[name] = append(r.Form[name], getFormValue(part))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
part, err = mr.NextPart()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateRedirect(r *http.Request, redirect string) bool {
|
|
||||||
if redirect != "" {
|
|
||||||
var redirectAllowTarget *regexp.Regexp
|
|
||||||
if REDIRECT_ALLOW_TARGET != "" {
|
|
||||||
redirectAllowTarget = regexp.MustCompile(REDIRECT_ALLOW_TARGET)
|
|
||||||
} else {
|
|
||||||
referer := r.Referer()
|
|
||||||
if referer == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
refererUrl, err := url.Parse(referer)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
redirectAllowTarget = regexp.MustCompile("^" + regexp.QuoteMeta(
|
|
||||||
refererUrl.Scheme+"://"+refererUrl.Host+"/",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
return redirectAllowTarget.MatchString(redirect)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func get(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.URL.Path == "/" {
|
|
||||||
http.Redirect(w, r, WEBSITE, http.StatusFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Use RequestURI instead of r.URL.Path, as we need the encoded form:
|
|
||||||
key := extractKey(r)
|
|
||||||
parts := strings.Split(key, "/")
|
|
||||||
if len(parts) == 3 {
|
|
||||||
context := appengine.NewContext(r)
|
|
||||||
item, err := memcache.Get(context, key)
|
|
||||||
if err == nil {
|
|
||||||
w.Header().Add("X-Content-Type-Options", "nosniff")
|
|
||||||
contentType, _ := url.QueryUnescape(parts[0])
|
|
||||||
if !imageTypes.MatchString(contentType) {
|
|
||||||
contentType = "application/octet-stream"
|
|
||||||
}
|
|
||||||
w.Header().Add("Content-Type", contentType)
|
|
||||||
w.Header().Add(
|
|
||||||
"Cache-Control",
|
|
||||||
fmt.Sprintf("public,max-age=%d", EXPIRATION_TIME),
|
|
||||||
)
|
|
||||||
w.Write(item.Value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
http.Error(w, "404 Not Found", http.StatusNotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
func post(w http.ResponseWriter, r *http.Request) {
|
|
||||||
result := make(map[string][]*FileInfo, 1)
|
|
||||||
result["files"] = handleUploads(r)
|
|
||||||
b, err := json.Marshal(result)
|
|
||||||
check(err)
|
|
||||||
if redirect := r.FormValue("redirect"); validateRedirect(r, redirect) {
|
|
||||||
if strings.Contains(redirect, "%s") {
|
|
||||||
redirect = fmt.Sprintf(
|
|
||||||
redirect,
|
|
||||||
escape(string(b)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
http.Redirect(w, r, redirect, http.StatusFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Header().Set("Cache-Control", "no-cache")
|
|
||||||
jsonType := "application/json"
|
|
||||||
if strings.Index(r.Header.Get("Accept"), jsonType) != -1 {
|
|
||||||
w.Header().Set("Content-Type", jsonType)
|
|
||||||
}
|
|
||||||
fmt.Fprintln(w, string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func delete(w http.ResponseWriter, r *http.Request) {
|
|
||||||
key := extractKey(r)
|
|
||||||
parts := strings.Split(key, "/")
|
|
||||||
if len(parts) == 3 {
|
|
||||||
result := make(map[string]bool, 1)
|
|
||||||
context := appengine.NewContext(r)
|
|
||||||
err := memcache.Delete(context, key)
|
|
||||||
if err == nil {
|
|
||||||
result[key] = true
|
|
||||||
contentType, _ := url.QueryUnescape(parts[0])
|
|
||||||
if imageTypes.MatchString(contentType) {
|
|
||||||
thumbnailKey := key + thumbSuffix + filepath.Ext(parts[2])
|
|
||||||
err := memcache.Delete(context, thumbnailKey)
|
|
||||||
if err == nil {
|
|
||||||
result[thumbnailKey] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
b, err := json.Marshal(result)
|
|
||||||
check(err)
|
|
||||||
fmt.Fprintln(w, string(b))
|
|
||||||
} else {
|
|
||||||
http.Error(w, "405 Method not allowed", http.StatusMethodNotAllowed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handle(w http.ResponseWriter, r *http.Request) {
|
|
||||||
params, err := url.ParseQuery(r.URL.RawQuery)
|
|
||||||
check(err)
|
|
||||||
w.Header().Add("Access-Control-Allow-Origin", "*")
|
|
||||||
w.Header().Add(
|
|
||||||
"Access-Control-Allow-Methods",
|
|
||||||
"OPTIONS, HEAD, GET, POST, DELETE",
|
|
||||||
)
|
|
||||||
w.Header().Add(
|
|
||||||
"Access-Control-Allow-Headers",
|
|
||||||
"Content-Type, Content-Range, Content-Disposition",
|
|
||||||
)
|
|
||||||
switch r.Method {
|
|
||||||
case "OPTIONS", "HEAD":
|
|
||||||
return
|
|
||||||
case "GET":
|
|
||||||
get(w, r)
|
|
||||||
case "POST":
|
|
||||||
if len(params["_method"]) > 0 && params["_method"][0] == "DELETE" {
|
|
||||||
delete(w, r)
|
|
||||||
} else {
|
|
||||||
post(w, r)
|
|
||||||
}
|
|
||||||
case "DELETE":
|
|
||||||
delete(w, r)
|
|
||||||
default:
|
|
||||||
http.Error(w, "501 Not Implemented", http.StatusNotImplemented)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
http.HandleFunc("/", handle)
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB |
|
@ -1,2 +0,0 @@
|
||||||
User-agent: *
|
|
||||||
Disallow:
|
|
|
@ -1,17 +0,0 @@
|
||||||
application: jquery-file-upload
|
|
||||||
version: 1
|
|
||||||
runtime: python27
|
|
||||||
api_version: 1
|
|
||||||
threadsafe: true
|
|
||||||
|
|
||||||
libraries:
|
|
||||||
- name: PIL
|
|
||||||
version: latest
|
|
||||||
|
|
||||||
handlers:
|
|
||||||
- url: /(favicon\.ico|robots\.txt)
|
|
||||||
static_files: static/\1
|
|
||||||
upload: static/(.*)
|
|
||||||
expiration: '1d'
|
|
||||||
- url: /.*
|
|
||||||
script: main.app
|
|
|
@ -1,204 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# jQuery File Upload Plugin GAE Python Example
|
|
||||||
# https://github.com/blueimp/jQuery-File-Upload
|
|
||||||
#
|
|
||||||
# Copyright 2011, Sebastian Tschan
|
|
||||||
# https://blueimp.net
|
|
||||||
#
|
|
||||||
# Licensed under the MIT license:
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
#
|
|
||||||
|
|
||||||
from google.appengine.api import memcache, images
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import urllib
|
|
||||||
import webapp2
|
|
||||||
|
|
||||||
DEBUG=os.environ.get('SERVER_SOFTWARE', '').startswith('Dev')
|
|
||||||
WEBSITE = 'https://blueimp.github.io/jQuery-File-Upload/'
|
|
||||||
MIN_FILE_SIZE = 1 # bytes
|
|
||||||
# Max file size is memcache limit (1MB) minus key size minus overhead:
|
|
||||||
MAX_FILE_SIZE = 999000 # bytes
|
|
||||||
IMAGE_TYPES = re.compile('image/(gif|p?jpeg|(x-)?png)')
|
|
||||||
ACCEPT_FILE_TYPES = IMAGE_TYPES
|
|
||||||
THUMB_MAX_WIDTH = 80
|
|
||||||
THUMB_MAX_HEIGHT = 80
|
|
||||||
THUMB_SUFFIX = '.'+str(THUMB_MAX_WIDTH)+'x'+str(THUMB_MAX_HEIGHT)+'.png'
|
|
||||||
EXPIRATION_TIME = 300 # seconds
|
|
||||||
# If set to None, only allow redirects to the referer protocol+host.
|
|
||||||
# Set to a regexp for custom pattern matching against the redirect value:
|
|
||||||
REDIRECT_ALLOW_TARGET = None
|
|
||||||
|
|
||||||
class CORSHandler(webapp2.RequestHandler):
|
|
||||||
def cors(self):
|
|
||||||
headers = self.response.headers
|
|
||||||
headers['Access-Control-Allow-Origin'] = '*'
|
|
||||||
headers['Access-Control-Allow-Methods'] =\
|
|
||||||
'OPTIONS, HEAD, GET, POST, DELETE'
|
|
||||||
headers['Access-Control-Allow-Headers'] =\
|
|
||||||
'Content-Type, Content-Range, Content-Disposition'
|
|
||||||
|
|
||||||
def initialize(self, request, response):
|
|
||||||
super(CORSHandler, self).initialize(request, response)
|
|
||||||
self.cors()
|
|
||||||
|
|
||||||
def json_stringify(self, obj):
|
|
||||||
return json.dumps(obj, separators=(',', ':'))
|
|
||||||
|
|
||||||
def options(self, *args, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class UploadHandler(CORSHandler):
|
|
||||||
def validate(self, file):
|
|
||||||
if file['size'] < MIN_FILE_SIZE:
|
|
||||||
file['error'] = 'File is too small'
|
|
||||||
elif file['size'] > MAX_FILE_SIZE:
|
|
||||||
file['error'] = 'File is too big'
|
|
||||||
elif not ACCEPT_FILE_TYPES.match(file['type']):
|
|
||||||
file['error'] = 'Filetype not allowed'
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def validate_redirect(self, redirect):
|
|
||||||
if redirect:
|
|
||||||
if REDIRECT_ALLOW_TARGET:
|
|
||||||
return REDIRECT_ALLOW_TARGET.match(redirect)
|
|
||||||
referer = self.request.headers['referer']
|
|
||||||
if referer:
|
|
||||||
from urlparse import urlparse
|
|
||||||
parts = urlparse(referer)
|
|
||||||
redirect_allow_target = '^' + re.escape(
|
|
||||||
parts.scheme + '://' + parts.netloc + '/'
|
|
||||||
)
|
|
||||||
return re.match(redirect_allow_target, redirect)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_file_size(self, file):
|
|
||||||
file.seek(0, 2) # Seek to the end of the file
|
|
||||||
size = file.tell() # Get the position of EOF
|
|
||||||
file.seek(0) # Reset the file position to the beginning
|
|
||||||
return size
|
|
||||||
|
|
||||||
def write_blob(self, data, info):
|
|
||||||
key = urllib.quote(info['type'].encode('utf-8'), '') +\
|
|
||||||
'/' + str(hash(data)) +\
|
|
||||||
'/' + urllib.quote(info['name'].encode('utf-8'), '')
|
|
||||||
try:
|
|
||||||
memcache.set(key, data, time=EXPIRATION_TIME)
|
|
||||||
except: #Failed to add to memcache
|
|
||||||
return (None, None)
|
|
||||||
thumbnail_key = None
|
|
||||||
if IMAGE_TYPES.match(info['type']):
|
|
||||||
try:
|
|
||||||
img = images.Image(image_data=data)
|
|
||||||
img.resize(
|
|
||||||
width=THUMB_MAX_WIDTH,
|
|
||||||
height=THUMB_MAX_HEIGHT
|
|
||||||
)
|
|
||||||
thumbnail_data = img.execute_transforms()
|
|
||||||
thumbnail_key = key + THUMB_SUFFIX
|
|
||||||
memcache.set(
|
|
||||||
thumbnail_key,
|
|
||||||
thumbnail_data,
|
|
||||||
time=EXPIRATION_TIME
|
|
||||||
)
|
|
||||||
except: #Failed to resize Image or add to memcache
|
|
||||||
thumbnail_key = None
|
|
||||||
return (key, thumbnail_key)
|
|
||||||
|
|
||||||
def handle_upload(self):
|
|
||||||
results = []
|
|
||||||
for name, fieldStorage in self.request.POST.items():
|
|
||||||
if type(fieldStorage) is unicode:
|
|
||||||
continue
|
|
||||||
result = {}
|
|
||||||
result['name'] = urllib.unquote(fieldStorage.filename)
|
|
||||||
result['type'] = fieldStorage.type
|
|
||||||
result['size'] = self.get_file_size(fieldStorage.file)
|
|
||||||
if self.validate(result):
|
|
||||||
key, thumbnail_key = self.write_blob(
|
|
||||||
fieldStorage.value,
|
|
||||||
result
|
|
||||||
)
|
|
||||||
if key is not None:
|
|
||||||
result['url'] = self.request.host_url + '/' + key
|
|
||||||
result['deleteUrl'] = result['url']
|
|
||||||
result['deleteType'] = 'DELETE'
|
|
||||||
if thumbnail_key is not None:
|
|
||||||
result['thumbnailUrl'] = self.request.host_url +\
|
|
||||||
'/' + thumbnail_key
|
|
||||||
else:
|
|
||||||
result['error'] = 'Failed to store uploaded file.'
|
|
||||||
results.append(result)
|
|
||||||
return results
|
|
||||||
|
|
||||||
def head(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
self.redirect(WEBSITE)
|
|
||||||
|
|
||||||
def post(self):
|
|
||||||
if (self.request.get('_method') == 'DELETE'):
|
|
||||||
return self.delete()
|
|
||||||
result = {'files': self.handle_upload()}
|
|
||||||
s = self.json_stringify(result)
|
|
||||||
redirect = self.request.get('redirect')
|
|
||||||
if self.validate_redirect(redirect):
|
|
||||||
return self.redirect(str(
|
|
||||||
redirect.replace('%s', urllib.quote(s, ''), 1)
|
|
||||||
))
|
|
||||||
if 'application/json' in self.request.headers.get('Accept'):
|
|
||||||
self.response.headers['Content-Type'] = 'application/json'
|
|
||||||
self.response.write(s)
|
|
||||||
|
|
||||||
class FileHandler(CORSHandler):
|
|
||||||
def normalize(self, str):
|
|
||||||
return urllib.quote(urllib.unquote(str), '')
|
|
||||||
|
|
||||||
def get(self, content_type, data_hash, file_name):
|
|
||||||
content_type = self.normalize(content_type)
|
|
||||||
file_name = self.normalize(file_name)
|
|
||||||
key = content_type + '/' + data_hash + '/' + file_name
|
|
||||||
data = memcache.get(key)
|
|
||||||
if data is None:
|
|
||||||
return self.error(404)
|
|
||||||
# Prevent browsers from MIME-sniffing the content-type:
|
|
||||||
self.response.headers['X-Content-Type-Options'] = 'nosniff'
|
|
||||||
content_type = urllib.unquote(content_type)
|
|
||||||
if not IMAGE_TYPES.match(content_type):
|
|
||||||
# Force a download dialog for non-image types:
|
|
||||||
content_type = 'application/octet-stream'
|
|
||||||
elif file_name.endswith(THUMB_SUFFIX):
|
|
||||||
content_type = 'image/png'
|
|
||||||
self.response.headers['Content-Type'] = content_type
|
|
||||||
# Cache for the expiration time:
|
|
||||||
self.response.headers['Cache-Control'] = 'public,max-age=%d' \
|
|
||||||
% EXPIRATION_TIME
|
|
||||||
self.response.write(data)
|
|
||||||
|
|
||||||
def delete(self, content_type, data_hash, file_name):
|
|
||||||
content_type = self.normalize(content_type)
|
|
||||||
file_name = self.normalize(file_name)
|
|
||||||
key = content_type + '/' + data_hash + '/' + file_name
|
|
||||||
result = {key: memcache.delete(key)}
|
|
||||||
content_type = urllib.unquote(content_type)
|
|
||||||
if IMAGE_TYPES.match(content_type):
|
|
||||||
thumbnail_key = key + THUMB_SUFFIX
|
|
||||||
result[thumbnail_key] = memcache.delete(thumbnail_key)
|
|
||||||
if 'application/json' in self.request.headers.get('Accept'):
|
|
||||||
self.response.headers['Content-Type'] = 'application/json'
|
|
||||||
s = self.json_stringify(result)
|
|
||||||
self.response.write(s)
|
|
||||||
|
|
||||||
app = webapp2.WSGIApplication(
|
|
||||||
[
|
|
||||||
('/', UploadHandler),
|
|
||||||
('/(.+)/([^/]+)/([^/]+)', FileHandler)
|
|
||||||
],
|
|
||||||
debug=DEBUG
|
|
||||||
)
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB |
|
@ -1,2 +0,0 @@
|
||||||
User-agent: *
|
|
||||||
Disallow:
|
|
|
@ -1,38 +0,0 @@
|
||||||
FROM php:7.0-apache
|
|
||||||
|
|
||||||
# Enable the Apache Headers module:
|
|
||||||
RUN ln -s /etc/apache2/mods-available/headers.load \
|
|
||||||
/etc/apache2/mods-enabled/headers.load
|
|
||||||
|
|
||||||
# Enable the Apache Rewrite module:
|
|
||||||
RUN ln -s /etc/apache2/mods-available/rewrite.load \
|
|
||||||
/etc/apache2/mods-enabled/rewrite.load
|
|
||||||
|
|
||||||
# Install GD, Imagick and ImageMagick as image conversion options:
|
|
||||||
RUN DEBIAN_FRONTEND=noninteractive \
|
|
||||||
apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
libpng-dev \
|
|
||||||
libjpeg-dev \
|
|
||||||
libmagickwand-dev \
|
|
||||||
imagemagick \
|
|
||||||
&& pecl install \
|
|
||||||
imagick \
|
|
||||||
&& docker-php-ext-enable \
|
|
||||||
imagick \
|
|
||||||
&& docker-php-ext-configure \
|
|
||||||
gd --with-jpeg-dir=/usr/include/ \
|
|
||||||
&& docker-php-ext-install \
|
|
||||||
gd \
|
|
||||||
# Uninstall obsolete packages:
|
|
||||||
&& apt-get autoremove -y \
|
|
||||||
libpng-dev \
|
|
||||||
libjpeg-dev \
|
|
||||||
libmagickwand-dev \
|
|
||||||
# Remove obsolete files:
|
|
||||||
&& apt-get clean \
|
|
||||||
&& rm -rf \
|
|
||||||
/tmp/* \
|
|
||||||
/usr/share/doc/* \
|
|
||||||
/var/cache/* \
|
|
||||||
/var/lib/apt/lists/* \
|
|
||||||
/var/tmp/*
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +0,0 @@
|
||||||
apache:
|
|
||||||
build: ./
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
volumes:
|
|
||||||
- "../../:/var/www/html"
|
|
|
@ -1,3 +0,0 @@
|
||||||
*
|
|
||||||
!.gitignore
|
|
||||||
!.htaccess
|
|
|
@ -1,26 +0,0 @@
|
||||||
# To enable the Headers module, execute the following command and reload Apache:
|
|
||||||
# sudo a2enmod headers
|
|
||||||
|
|
||||||
# The following directives prevent the execution of script files
|
|
||||||
# in the context of the website.
|
|
||||||
# They also force the content-type application/octet-stream and
|
|
||||||
# force browsers to display a download dialog for non-image files.
|
|
||||||
SetHandler default-handler
|
|
||||||
ForceType application/octet-stream
|
|
||||||
Header set Content-Disposition attachment
|
|
||||||
|
|
||||||
# The following unsets the forced type and Content-Disposition headers
|
|
||||||
# for known image files:
|
|
||||||
<FilesMatch "(?i)\.(gif|jpe?g|png)$">
|
|
||||||
ForceType none
|
|
||||||
Header unset Content-Disposition
|
|
||||||
</FilesMatch>
|
|
||||||
|
|
||||||
# The following directive prevents browsers from MIME-sniffing the content-type.
|
|
||||||
# This is an important complement to the ForceType directive above:
|
|
||||||
Header set X-Content-Type-Options nosniff
|
|
||||||
|
|
||||||
# Uncomment the following lines to prevent unauthorized download of files:
|
|
||||||
#AuthName "Authorization required"
|
|
||||||
#AuthType Basic
|
|
||||||
#require valid-user
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
/*
|
|
||||||
* jQuery File Upload Plugin PHP Example
|
|
||||||
* https://github.com/blueimp/jQuery-File-Upload
|
|
||||||
*
|
|
||||||
* Copyright 2010, Sebastian Tschan
|
|
||||||
* https://blueimp.net
|
|
||||||
*
|
|
||||||
* Licensed under the MIT license:
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
error_reporting(E_ALL | E_STRICT);
|
|
||||||
require('UploadHandler.php');
|
|
||||||
$upload_handler = new UploadHandler();
|
|
Loading…
Reference in a new issue