mirror of
https://gitee.com/coder-xiaomo/flashsale
synced 2025-09-13 15:31:38 +08:00
添加Metronic(作为LFS)
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* jQuery File Upload Plugin GAE Go Example 3.1.1
|
||||
* https://github.com/blueimp/jQuery-File-Upload
|
||||
*
|
||||
* Copyright 2011, Sebastian Tschan
|
||||
* https://blueimp.net
|
||||
*
|
||||
* Licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"appengine"
|
||||
"appengine/blobstore"
|
||||
"appengine/image"
|
||||
"appengine/taskqueue"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
WEBSITE = "http://blueimp.github.io/jQuery-File-Upload/"
|
||||
MIN_FILE_SIZE = 1 // bytes
|
||||
MAX_FILE_SIZE = 5000000 // bytes
|
||||
IMAGE_TYPES = "image/(gif|p?jpeg|(x-)?png)"
|
||||
ACCEPT_FILE_TYPES = IMAGE_TYPES
|
||||
EXPIRATION_TIME = 300 // seconds
|
||||
THUMBNAIL_PARAM = "=s80"
|
||||
)
|
||||
|
||||
var (
|
||||
imageTypes = regexp.MustCompile(IMAGE_TYPES)
|
||||
acceptFileTypes = regexp.MustCompile(ACCEPT_FILE_TYPES)
|
||||
)
|
||||
|
||||
type FileInfo struct {
|
||||
Key appengine.BlobKey `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 appengine.Context) {
|
||||
u := &url.URL{
|
||||
Scheme: r.URL.Scheme,
|
||||
Host: appengine.DefaultVersionHostname(c),
|
||||
Path: "/",
|
||||
}
|
||||
uString := u.String()
|
||||
fi.Url = uString + escape(string(fi.Key)) + "/" +
|
||||
escape(string(fi.Name))
|
||||
fi.DeleteUrl = fi.Url + "?delete=true"
|
||||
fi.DeleteType = "DELETE"
|
||||
if imageTypes.MatchString(fi.Type) {
|
||||
servingUrl, err := image.ServingURL(
|
||||
c,
|
||||
fi.Key,
|
||||
&image.ServingURLOptions{
|
||||
Secure: strings.HasSuffix(u.Scheme, "s"),
|
||||
Size: 0,
|
||||
Crop: false,
|
||||
},
|
||||
)
|
||||
check(err)
|
||||
fi.ThumbnailUrl = servingUrl.String() + THUMBNAIL_PARAM
|
||||
}
|
||||
}
|
||||
|
||||
func check(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func escape(s string) string {
|
||||
return strings.Replace(url.QueryEscape(s), "+", "%20", -1)
|
||||
}
|
||||
|
||||
func delayedDelete(c appengine.Context, fi *FileInfo) {
|
||||
if key := string(fi.Key); key != "" {
|
||||
task := &taskqueue.Task{
|
||||
Path: "/" + escape(key) + "/-",
|
||||
Method: "DELETE",
|
||||
Delay: time.Duration(EXPIRATION_TIME) * time.Second,
|
||||
}
|
||||
taskqueue.Add(c, task, "")
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}()
|
||||
lr := &io.LimitedReader{R: p, N: MAX_FILE_SIZE + 1}
|
||||
context := appengine.NewContext(r)
|
||||
w, err := blobstore.Create(context, fi.Type)
|
||||
defer func() {
|
||||
w.Close()
|
||||
fi.Size = MAX_FILE_SIZE + 1 - lr.N
|
||||
fi.Key, err = w.Key()
|
||||
check(err)
|
||||
if !fi.ValidateSize() {
|
||||
err := blobstore.Delete(context, fi.Key)
|
||||
check(err)
|
||||
return
|
||||
}
|
||||
delayedDelete(context, fi)
|
||||
fi.CreateUrls(r, context)
|
||||
}()
|
||||
check(err)
|
||||
_, err = io.Copy(w, lr)
|
||||
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 get(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
http.Redirect(w, r, WEBSITE, http.StatusFound)
|
||||
return
|
||||
}
|
||||
parts := strings.Split(r.URL.Path, "/")
|
||||
if len(parts) == 3 {
|
||||
if key := parts[1]; key != "" {
|
||||
blobKey := appengine.BlobKey(key)
|
||||
bi, err := blobstore.Stat(appengine.NewContext(r), blobKey)
|
||||
if err == nil {
|
||||
w.Header().Add("X-Content-Type-Options", "nosniff")
|
||||
if !imageTypes.MatchString(bi.ContentType) {
|
||||
w.Header().Add("Content-Type", "application/octet-stream")
|
||||
w.Header().Add(
|
||||
"Content-Disposition",
|
||||
fmt.Sprintf("attachment; filename=\"%s\"", parts[2]),
|
||||
)
|
||||
}
|
||||
w.Header().Add(
|
||||
"Cache-Control",
|
||||
fmt.Sprintf("public,max-age=%d", EXPIRATION_TIME),
|
||||
)
|
||||
blobstore.Send(w, blobKey)
|
||||
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"); 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) {
|
||||
parts := strings.Split(r.URL.Path, "/")
|
||||
if len(parts) != 3 {
|
||||
return
|
||||
}
|
||||
result := make(map[string]bool, 1)
|
||||
if key := parts[1]; key != "" {
|
||||
c := appengine.NewContext(r)
|
||||
blobKey := appengine.BlobKey(key)
|
||||
err := blobstore.Delete(c, blobKey)
|
||||
check(err)
|
||||
err = image.DeleteServingURL(c, blobKey)
|
||||
check(err)
|
||||
result[key] = true
|
||||
}
|
||||
jsonType := "application/json"
|
||||
if strings.Index(r.Header.Get("Accept"), jsonType) != -1 {
|
||||
w.Header().Set("Content-Type", jsonType)
|
||||
}
|
||||
b, err := json.Marshal(result)
|
||||
check(err)
|
||||
fmt.Fprintln(w, string(b))
|
||||
}
|
||||
|
||||
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, PUT, DELETE",
|
||||
)
|
||||
w.Header().Add(
|
||||
"Access-Control-Allow-Headers",
|
||||
"Content-Type, Content-Range, Content-Disposition",
|
||||
)
|
||||
switch r.Method {
|
||||
case "OPTIONS":
|
||||
case "HEAD":
|
||||
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)
|
||||
}
|
Reference in New Issue
Block a user