mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
Add tutorial for DropzoneJS. Stay tuned for two-parts article series on https://medium.com/@kataras and https://dev.to/@kataras
Former-commit-id: 2aac5c1f5a5b5a7039a7a5fad3312a49e9311ddd
This commit is contained in:
parent
c6a3b47573
commit
000d1b000d
|
@ -16,16 +16,16 @@ func main() {
|
|||
|
||||
app.RegisterView(iris.HTML("./templates", ".html"))
|
||||
|
||||
// Serve the form.html to the user
|
||||
// Serve the upload_form.html to the client.
|
||||
app.Get("/upload", func(ctx iris.Context) {
|
||||
//create a token (optionally)
|
||||
// create a token (optionally).
|
||||
|
||||
now := time.Now().Unix()
|
||||
h := md5.New()
|
||||
io.WriteString(h, strconv.FormatInt(now, 10))
|
||||
token := fmt.Sprintf("%x", h.Sum(nil))
|
||||
|
||||
// render the form with the token for any use you like
|
||||
// render the form with the token for any use you'd like.
|
||||
ctx.ViewData("", token)
|
||||
ctx.View("upload_form.html")
|
||||
})
|
||||
|
@ -34,7 +34,7 @@ func main() {
|
|||
app.Post("/upload", iris.LimitRequestBodySize(10<<20),
|
||||
func(ctx iris.Context) {
|
||||
// or use ctx.SetMaxRequestBodySize(10 << 20)
|
||||
//to limit the uploaded file(s) size.
|
||||
// to limit the uploaded file(s) size.
|
||||
|
||||
// Get the file from the request
|
||||
file, info, err := ctx.FormFile("uploadfile")
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<form enctype="multipart/form-data"
|
||||
action="http://127.0.0.1:8080/upload" method="post">
|
||||
action="http://127.0.0.1:8080/upload" method="POST">
|
||||
<input type="file" name="uploadfile" /> <input type="hidden"
|
||||
name="token" value="{{.}}" /> <input type="submit" value="upload" />
|
||||
</form>
|
||||
|
|
160
_examples/tutorial/dropzonejs/README.md
Normal file
160
_examples/tutorial/dropzonejs/README.md
Normal file
|
@ -0,0 +1,160 @@
|
|||
This is the part 1 of 2 in DropzoneJS + Go series.
|
||||
|
||||
- [Part 1: How to build a file upload form](README.md)
|
||||
- [Part 2: How to display existing files on server](README_NEXT.md)
|
||||
|
||||
# DropzoneJS + Go: How to build a file upload form
|
||||
|
||||
[DropzoneJS](https://github.com/enyo/dropzone) is an open source library that provides drag'n'drop file uploads with image previews. It is a great JavaScript library which actually does not even rely on JQuery.
|
||||
In this tutorial, we are building a multiple file upload form using DropzoneJS, and the backend will be handled by Go and [Iris](https://iris-go.com).
|
||||
|
||||
## Table Of Content
|
||||
|
||||
- [Preparation](#preparation)
|
||||
- [Work with DropzoneJS](#work-with-dropzonejs)
|
||||
- [Work with Go](#work-with-go)
|
||||
|
||||
## Preparation
|
||||
|
||||
1. Download [Go(Golang)](https://golang.org/dl), setup your computer as shown there and continue to 2.
|
||||
2. Install [Iris](https://github.com/kataras/iris); open a terminal and execute `go get -u github.com/kataras/iris`
|
||||
3. Download DropzoneJS from [this URL](https://raw.githubusercontent.com/enyo/dropzone/master/dist/dropzone.js). DropzoneJS does not rely on JQuery, you will not have to worry that, upgrading JQuery version breaks your application.
|
||||
4. Download dropzone.css from [this URL](https://raw.githubusercontent.com/enyo/dropzone/master/dist/dropzone.css), if you want some already made css.
|
||||
5. Create a folder "./public/uploads", this is for storing uploaded files.
|
||||
6. Create a file "./views/upload.html", this is for the front form page.
|
||||
7. Create a file "./main.go", this is for handling backend file upload process.
|
||||
|
||||
Your folder&file structure should look like this after the preparation:
|
||||
|
||||
![folder&file structure](folder_structure.png)
|
||||
|
||||
## Work with DropzoneJS
|
||||
|
||||
Open file "./views/upload.html" and let us create a DropzoneJs form.
|
||||
|
||||
Copy the content below to "./views/upload.html" and we will go through each line of code individually.
|
||||
|
||||
```html
|
||||
<!-- /views/upload.html -->
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>DropzoneJS Uploader</title>
|
||||
|
||||
<!-- 1 -->
|
||||
<link href="/public/css/dropzone.css" type="text/css" rel="stylesheet" />
|
||||
|
||||
<!-- 2 -->
|
||||
<script src="/public/js/dropzone.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- 3 -->
|
||||
<form action="/upload" method="POST" class="dropzone" id="my-dropzone">
|
||||
<div class="fallback">
|
||||
<input name="file" type="file" multiple />
|
||||
<input type="submit" value="Upload" />
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
1. Include the CSS Stylesheet.
|
||||
2. Include DropzoneJS JavaScript library.
|
||||
3. Create an upload form with css class "dropzone" and "action" is the route path "/upload". Note that we did create an input filed for fallback mode. This is all handled by DropzoneJS library itself. All we need to do is assign css class "dropzone" to the form. By default, DropzoneJS will find all forms with class "dropzone" and automatically attach itself to it.
|
||||
|
||||
## Work with Go
|
||||
|
||||
Now you have come to Last part of the tutorial. In this section, we will store files sent from DropzoneJS to the "./public/uploads" folder.
|
||||
|
||||
Open "main.go" and copy the code below:
|
||||
|
||||
```go
|
||||
// main.go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
const uploadsDir = "./public/uploads/"
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
// Register templates
|
||||
app.RegisterView(iris.HTML("./views", ".html"))
|
||||
|
||||
// Make the /public route path to statically serve the ./public/... contents
|
||||
app.StaticWeb("/public", "./public")
|
||||
|
||||
// Render the actual form
|
||||
// GET: http://localhost:8080
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.View("upload.html")
|
||||
})
|
||||
|
||||
// Upload the file to the server
|
||||
// POST: http://localhost:8080/upload
|
||||
app.Post("/upload", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) {
|
||||
// Get the file from the dropzone request
|
||||
file, info, err := ctx.FormFile("file")
|
||||
if err != nil {
|
||||
ctx.StatusCode(iris.StatusInternalServerError)
|
||||
ctx.Application().Logger().Warnf("Error while uploading: %v", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
fname := info.Filename
|
||||
|
||||
// Create a file with the same name
|
||||
// assuming that you have a folder named 'uploads'
|
||||
out, err := os.OpenFile(uploadsDir+fname,
|
||||
os.O_WRONLY|os.O_CREATE, 0666)
|
||||
|
||||
if err != nil {
|
||||
ctx.StatusCode(iris.StatusInternalServerError)
|
||||
ctx.Application().Logger().Warnf("Error while preparing the new file: %v", err.Error())
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
io.Copy(out, file)
|
||||
})
|
||||
|
||||
// Start the server at http://localhost:8080
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
```
|
||||
|
||||
1. Create a new Iris app.
|
||||
2. Register and load templates from the "views" folder.
|
||||
3. Make the "/public" route path to statically serve the ./public/... folder's contents
|
||||
4. Create a route to serve the upload form.
|
||||
5. Create a route to handle the POST form data from the DropzoneJS' form
|
||||
6. Declare a variable for destination folder.
|
||||
7. If file is sent to the page, store the file object to a temporary "file" variable.
|
||||
8. Move uploaded file to destination based on the uploadsDir+uploaded file's name.
|
||||
|
||||
**Run the server**
|
||||
Open the terminal at the current project's folder and execute:
|
||||
|
||||
```bash
|
||||
$ go run main.go
|
||||
Now listening on: http://localhost:8080
|
||||
Application started. Press CTRL+C to shut down.
|
||||
```
|
||||
|
||||
Now go to browser, and navigate to http://localhost:8080, you should be able to see a page as below:
|
||||
|
||||
![no files screenshot](no_files.png)
|
||||
![with uploaded files screenshot](with_files.png)
|
303
_examples/tutorial/dropzonejs/README_NEXT.MD
Normal file
303
_examples/tutorial/dropzonejs/README_NEXT.MD
Normal file
|
@ -0,0 +1,303 @@
|
|||
This is the part 2 of 2 in DropzoneJS + Go series.
|
||||
|
||||
- [Part 1: How to build a file upload form](README.md)
|
||||
- [Part 2: How to display existing files on server](README_NEXT.md)
|
||||
|
||||
# DropzoneJS + Go: How to display existing files on server
|
||||
|
||||
In this tutorial, we will show you how to display existing files on the server when using DropzoneJs and Go. This tutorial is based on [How to build a file upload form using DropzoneJs and Go](README.md). Make sure you have read it before proceeding to content in this tutorial.
|
||||
|
||||
## Table Of Content
|
||||
|
||||
- [Preparation](#preparation)
|
||||
- [Modify the Server side](#modify-the-server-side)
|
||||
- [Modify the Client side](#modify-the-client-side)
|
||||
- [References](#references)
|
||||
- [The End](#the-end)
|
||||
|
||||
## Preparation
|
||||
|
||||
Install the go package "nfnt/resize" with `go get github.com/nfnt/resize`, we need it to create thumbnails.
|
||||
|
||||
In previous [tutorial](README.md). We have already set up a proper working DropzoneJs upload form. There is no additional file needed for this tutorial. What we need to do is to make some modifications to file below:
|
||||
|
||||
1. main.go
|
||||
2. views/upload.html
|
||||
|
||||
Let us get started!
|
||||
|
||||
## Modify the Server side
|
||||
|
||||
In previous tutorial. All "/upload" does is to store uploaded files to the server directory "./public/uploads". So we need to add a piece of code to retrieve stored files' information (name and size), and return it in JSON format.
|
||||
|
||||
Copy the content below to "main.go". Read comments for details.
|
||||
|
||||
```go
|
||||
// main.go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
|
||||
"github.com/nfnt/resize" // $ go get -u github.com/nfnt/resize
|
||||
)
|
||||
|
||||
const uploadsDir = "./public/uploads/"
|
||||
|
||||
type uploadedFile struct {
|
||||
// {name: "", size: } are the dropzone's only requirements.
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
type uploadedFiles struct {
|
||||
dir string
|
||||
items []uploadedFile
|
||||
mu sync.RWMutex // slices are safe but RWMutex is a good practise for you.
|
||||
}
|
||||
|
||||
// scan the ./public/uploads folder for any files
|
||||
// add them to a new uploadedFiles list.
|
||||
func scanUploads(dir string) *uploadedFiles {
|
||||
f := new(uploadedFiles)
|
||||
|
||||
lindex := dir[len(dir)-1]
|
||||
if lindex != os.PathSeparator && lindex != '/' {
|
||||
dir += string(os.PathSeparator)
|
||||
}
|
||||
|
||||
// create directories if necessary
|
||||
// and if, then return empty uploaded files; skipping the scan.
|
||||
if err := os.MkdirAll(dir, os.FileMode(0666)); err != nil {
|
||||
return f
|
||||
}
|
||||
|
||||
// otherwise scan the given "dir" for files.
|
||||
f.scan(dir)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *uploadedFiles) scan(dir string) {
|
||||
f.dir = dir
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
|
||||
// if it's directory or a thumbnail we saved ealier, skip it.
|
||||
if info.IsDir() || strings.HasPrefix(info.Name(), "thumbnail_") {
|
||||
return nil
|
||||
}
|
||||
|
||||
f.add(info.Name(), info.Size())
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// add the file's Name and Size to the uploadedFiles memory list
|
||||
func (f *uploadedFiles) add(name string, size int64) uploadedFile {
|
||||
f.mu.Lock()
|
||||
uf := uploadedFile{
|
||||
Name: name,
|
||||
Size: size,
|
||||
}
|
||||
f.items = append(f.items, uf)
|
||||
f.mu.Unlock()
|
||||
|
||||
return uf
|
||||
}
|
||||
|
||||
// create thumbnail 100x100
|
||||
// and save that to the ./public/uploads/thumbnail_$FILENAME
|
||||
func (f *uploadedFiles) createThumbnail(uf uploadedFile) {
|
||||
file, err := os.Open(path.Join(f.dir, uf.Name))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
name := strings.ToLower(uf.Name)
|
||||
|
||||
out, err := os.OpenFile(f.dir+"thumbnail_"+uf.Name,
|
||||
os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
if strings.HasSuffix(name, ".jpg") {
|
||||
// decode jpeg into image.Image
|
||||
img, err := jpeg.Decode(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// write new image to file
|
||||
resized := resize.Thumbnail(180, 180, img, resize.Lanczos3)
|
||||
jpeg.Encode(out, resized,
|
||||
&jpeg.Options{Quality: jpeg.DefaultQuality})
|
||||
|
||||
} else if strings.HasSuffix(name, ".png") {
|
||||
img, err := png.Decode(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// write new image to file
|
||||
resized := resize.Thumbnail(180, 180, img, resize.Lanczos3) // slower but better res
|
||||
png.Encode(out, resized)
|
||||
}
|
||||
// and so on... you got the point, this code can be simplify, as a practise.
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.RegisterView(iris.HTML("./views", ".html"))
|
||||
|
||||
app.StaticWeb("/public", "./public")
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.View("upload.html")
|
||||
})
|
||||
|
||||
files := scanUploads(uploadsDir)
|
||||
|
||||
app.Get("/uploads", func(ctx iris.Context) {
|
||||
ctx.JSON(files.items)
|
||||
})
|
||||
|
||||
app.Post("/upload", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) {
|
||||
// Get the file from the dropzone request
|
||||
file, info, err := ctx.FormFile("file")
|
||||
if err != nil {
|
||||
ctx.StatusCode(iris.StatusInternalServerError)
|
||||
ctx.Application().Logger().Warnf("Error while uploading: %v", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
fname := info.Filename
|
||||
|
||||
// Create a file with the same name
|
||||
// assuming that you have a folder named 'uploads'
|
||||
out, err := os.OpenFile(uploadsDir+fname,
|
||||
os.O_WRONLY|os.O_CREATE, 0666)
|
||||
|
||||
if err != nil {
|
||||
ctx.StatusCode(iris.StatusInternalServerError)
|
||||
ctx.Application().Logger().Warnf("Error while preparing the new file: %v", err.Error())
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
io.Copy(out, file)
|
||||
|
||||
// optionally, add that file to the list in order to be visible when refresh.
|
||||
uploadedFile := files.add(fname, info.Size)
|
||||
go files.createThumbnail(uploadedFile)
|
||||
})
|
||||
|
||||
// start the server at http://localhost:8080
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
```
|
||||
|
||||
## Modify the Client side
|
||||
|
||||
Copy content below to "./views/upload.html". We will go through modifications individually.
|
||||
|
||||
```html
|
||||
<!-- /views/upload.html -->
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>DropzoneJS Uploader</title>
|
||||
|
||||
<!-- 1 -->
|
||||
<link href="/public/css/dropzone.css" type="text/css" rel="stylesheet" />
|
||||
|
||||
<!-- 2 -->
|
||||
<script src="/public/js/dropzone.js"></script>
|
||||
<!-- 4 -->
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
|
||||
<!-- 5 -->
|
||||
<script>
|
||||
Dropzone.options.myDropzone = {
|
||||
paramName: "file", // The name that will be used to transfer the file
|
||||
init: function () {
|
||||
thisDropzone = this;
|
||||
// 6
|
||||
$.get('/uploads', function (data) {
|
||||
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
// 7
|
||||
$.each(data, function (key, value) {
|
||||
var mockFile = { name: value.name, size: value.size };
|
||||
|
||||
thisDropzone.emit("addedfile", mockFile);
|
||||
thisDropzone.options.thumbnail.call(thisDropzone, mockFile, '/public/uploads/thumbnail_' + value.name);
|
||||
|
||||
// Make sure that there is no progress bar, etc...
|
||||
thisDropzone.emit("complete", mockFile);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- 3 -->
|
||||
<form action="/upload" method="POST" class="dropzone" id="my-dropzone">
|
||||
<div class="fallback">
|
||||
<input name="file" type="file" multiple />
|
||||
<input type="submit" value="Upload" />
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
1. We added Jquery library into our page. This actually not for DropzoneJs directly. We are using Jquery's ajax function **$.get** only. You will see below
|
||||
2. We added an ID element (my-dropzone) to the form. This is needed because we need to pass configuration values to Dropzone. And to do it, we must have an ID reference of it. So that we can configure it by assigning values to Dropzone.options.myDropzone. A lot of people face confusion when configuring Dropzone. To put it in a simple way. Do not take Dropzone as a Jquery plugin, it has its own syntax and you need to follow it.
|
||||
3. This starts the main part of modification. What we did here is to pass a function to listen to Dropzone's init event. This event is called when Dropzone is initialized.
|
||||
4. Retrieve files details from the new "/uploads" via ajax.
|
||||
5. Create mockFile using values from server. mockFile is simply JavaScript objects with properties of name and size. Then we call Dropzone's **addedfile** and **thumbnail** functions explicitly to put existing files to Dropzone upload area and generate its thumbnail.
|
||||
|
||||
|
||||
**Run the server**
|
||||
Open the terminal at the current project's folder and execute:
|
||||
|
||||
```bash
|
||||
$ go run main.go
|
||||
Now listening on: http://localhost:8080
|
||||
Application started. Press CTRL+C to shut down.
|
||||
```
|
||||
|
||||
If you have done it successfully. Now go and upload some images and reload the upload page. Already uploaded files should auto display in Dropzone area.
|
||||
|
||||
![with uploaded files screenshot](with_files.png)
|
||||
|
||||
## References
|
||||
|
||||
- http://www.dropzonejs.com/#server-side-implementation
|
||||
- https://www.startutorial.com/articles/view/how-to-build-a-file-upload-form-using-dropzonejs-and-php
|
||||
- https://docs.iris-go.com
|
||||
- https://github.com/kataras/iris/tree/master/_examples/tutorial/dropzonejs
|
||||
|
||||
## The end
|
||||
|
||||
Hopefully this simple tutorial helped you with your development.
|
||||
If you like my post, please follow me on [Twitter](https://twitter.com/makismaropoulos) and help spread the word. I need your support to continue.
|
BIN
_examples/tutorial/dropzonejs/folder_structure.png
Normal file
BIN
_examples/tutorial/dropzonejs/folder_structure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
_examples/tutorial/dropzonejs/no_files.png
Normal file
BIN
_examples/tutorial/dropzonejs/no_files.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
171
_examples/tutorial/dropzonejs/src/main.go
Normal file
171
_examples/tutorial/dropzonejs/src/main.go
Normal file
|
@ -0,0 +1,171 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
|
||||
"github.com/nfnt/resize"
|
||||
)
|
||||
|
||||
// $ go get -u github.com/nfnt/resize
|
||||
|
||||
const uploadsDir = "./public/uploads/"
|
||||
|
||||
type uploadedFile struct {
|
||||
// {name: "", size: } are the dropzone's only requirements.
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
type uploadedFiles struct {
|
||||
dir string
|
||||
items []uploadedFile
|
||||
mu sync.RWMutex // slices are safe but RWMutex is a good practise for you.
|
||||
}
|
||||
|
||||
func scanUploads(dir string) *uploadedFiles {
|
||||
|
||||
f := new(uploadedFiles)
|
||||
|
||||
lindex := dir[len(dir)-1]
|
||||
if lindex != os.PathSeparator && lindex != '/' {
|
||||
dir += string(os.PathSeparator)
|
||||
}
|
||||
|
||||
// create directories if necessary
|
||||
// and if, then return empty uploaded files; skipping the scan.
|
||||
if err := os.MkdirAll(dir, os.FileMode(0666)); err != nil {
|
||||
return f
|
||||
}
|
||||
|
||||
// otherwise scan the given "dir" for files.
|
||||
f.scan(dir)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *uploadedFiles) scan(dir string) {
|
||||
f.dir = dir
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
|
||||
// if it's directory or a thumbnail we saved ealier, skip it.
|
||||
if info.IsDir() || strings.HasPrefix(info.Name(), "thumbnail_") {
|
||||
return nil
|
||||
}
|
||||
|
||||
f.add(info.Name(), info.Size())
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (f *uploadedFiles) add(name string, size int64) uploadedFile {
|
||||
f.mu.Lock()
|
||||
uf := uploadedFile{
|
||||
Name: name,
|
||||
Size: size,
|
||||
}
|
||||
f.items = append(f.items, uf)
|
||||
f.mu.Unlock()
|
||||
|
||||
return uf
|
||||
}
|
||||
|
||||
func (f *uploadedFiles) createThumbnail(uf uploadedFile) {
|
||||
file, err := os.Open(path.Join(f.dir, uf.Name))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
name := strings.ToLower(uf.Name)
|
||||
|
||||
out, err := os.OpenFile(f.dir+"thumbnail_"+uf.Name,
|
||||
os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
if strings.HasSuffix(name, ".jpg") {
|
||||
// decode jpeg into image.Image
|
||||
img, err := jpeg.Decode(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// write new image to file
|
||||
resized := resize.Thumbnail(180, 180, img, resize.Lanczos3)
|
||||
jpeg.Encode(out, resized,
|
||||
&jpeg.Options{Quality: jpeg.DefaultQuality})
|
||||
|
||||
} else if strings.HasSuffix(name, ".png") {
|
||||
img, err := png.Decode(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// write new image to file
|
||||
resized := resize.Thumbnail(180, 180, img, resize.Lanczos3) // slower but better res
|
||||
png.Encode(out, resized)
|
||||
}
|
||||
// and so on... you got the point, this code can be simplify, as a practise.
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.RegisterView(iris.HTML("./views", ".html"))
|
||||
|
||||
app.StaticWeb("/public", "./public")
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.View("upload.html")
|
||||
})
|
||||
|
||||
files := scanUploads(uploadsDir)
|
||||
|
||||
app.Get("/uploads", func(ctx iris.Context) {
|
||||
ctx.JSON(files.items)
|
||||
})
|
||||
|
||||
app.Post("/upload", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) {
|
||||
// Get the file from the dropzone request
|
||||
file, info, err := ctx.FormFile("file")
|
||||
if err != nil {
|
||||
ctx.StatusCode(iris.StatusInternalServerError)
|
||||
ctx.Application().Logger().Warnf("Error while uploading: %v", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
fname := info.Filename
|
||||
|
||||
// Create a file with the same name
|
||||
// assuming that you have a folder named 'uploads'
|
||||
out, err := os.OpenFile(uploadsDir+fname,
|
||||
os.O_WRONLY|os.O_CREATE, 0666)
|
||||
|
||||
if err != nil {
|
||||
ctx.StatusCode(iris.StatusInternalServerError)
|
||||
ctx.Application().Logger().Warnf("Error while preparing the new file: %v", err.Error())
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
io.Copy(out, file)
|
||||
|
||||
// optionally, add that file to the list in order to be visible when refresh.
|
||||
uploadedFile := files.add(fname, info.Size)
|
||||
go files.createThumbnail(uploadedFile)
|
||||
})
|
||||
|
||||
// start the server at http://localhost:8080
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
388
_examples/tutorial/dropzonejs/src/public/css/dropzone.css
Normal file
388
_examples/tutorial/dropzonejs/src/public/css/dropzone.css
Normal file
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
* The MIT License
|
||||
* Copyright (c) 2012 Matias Meno <m@tias.me>
|
||||
*/
|
||||
@-webkit-keyframes passing-through {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(40px);
|
||||
-moz-transform: translateY(40px);
|
||||
-ms-transform: translateY(40px);
|
||||
-o-transform: translateY(40px);
|
||||
transform: translateY(40px); }
|
||||
30%, 70% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0px);
|
||||
-moz-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
-o-transform: translateY(0px);
|
||||
transform: translateY(0px); }
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(-40px);
|
||||
-moz-transform: translateY(-40px);
|
||||
-ms-transform: translateY(-40px);
|
||||
-o-transform: translateY(-40px);
|
||||
transform: translateY(-40px); } }
|
||||
@-moz-keyframes passing-through {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(40px);
|
||||
-moz-transform: translateY(40px);
|
||||
-ms-transform: translateY(40px);
|
||||
-o-transform: translateY(40px);
|
||||
transform: translateY(40px); }
|
||||
30%, 70% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0px);
|
||||
-moz-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
-o-transform: translateY(0px);
|
||||
transform: translateY(0px); }
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(-40px);
|
||||
-moz-transform: translateY(-40px);
|
||||
-ms-transform: translateY(-40px);
|
||||
-o-transform: translateY(-40px);
|
||||
transform: translateY(-40px); } }
|
||||
@keyframes passing-through {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(40px);
|
||||
-moz-transform: translateY(40px);
|
||||
-ms-transform: translateY(40px);
|
||||
-o-transform: translateY(40px);
|
||||
transform: translateY(40px); }
|
||||
30%, 70% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0px);
|
||||
-moz-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
-o-transform: translateY(0px);
|
||||
transform: translateY(0px); }
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(-40px);
|
||||
-moz-transform: translateY(-40px);
|
||||
-ms-transform: translateY(-40px);
|
||||
-o-transform: translateY(-40px);
|
||||
transform: translateY(-40px); } }
|
||||
@-webkit-keyframes slide-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(40px);
|
||||
-moz-transform: translateY(40px);
|
||||
-ms-transform: translateY(40px);
|
||||
-o-transform: translateY(40px);
|
||||
transform: translateY(40px); }
|
||||
30% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0px);
|
||||
-moz-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
-o-transform: translateY(0px);
|
||||
transform: translateY(0px); } }
|
||||
@-moz-keyframes slide-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(40px);
|
||||
-moz-transform: translateY(40px);
|
||||
-ms-transform: translateY(40px);
|
||||
-o-transform: translateY(40px);
|
||||
transform: translateY(40px); }
|
||||
30% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0px);
|
||||
-moz-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
-o-transform: translateY(0px);
|
||||
transform: translateY(0px); } }
|
||||
@keyframes slide-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(40px);
|
||||
-moz-transform: translateY(40px);
|
||||
-ms-transform: translateY(40px);
|
||||
-o-transform: translateY(40px);
|
||||
transform: translateY(40px); }
|
||||
30% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0px);
|
||||
-moz-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
-o-transform: translateY(0px);
|
||||
transform: translateY(0px); } }
|
||||
@-webkit-keyframes pulse {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
-o-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
10% {
|
||||
-webkit-transform: scale(1.1);
|
||||
-moz-transform: scale(1.1);
|
||||
-ms-transform: scale(1.1);
|
||||
-o-transform: scale(1.1);
|
||||
transform: scale(1.1); }
|
||||
20% {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
-o-transform: scale(1);
|
||||
transform: scale(1); } }
|
||||
@-moz-keyframes pulse {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
-o-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
10% {
|
||||
-webkit-transform: scale(1.1);
|
||||
-moz-transform: scale(1.1);
|
||||
-ms-transform: scale(1.1);
|
||||
-o-transform: scale(1.1);
|
||||
transform: scale(1.1); }
|
||||
20% {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
-o-transform: scale(1);
|
||||
transform: scale(1); } }
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
-o-transform: scale(1);
|
||||
transform: scale(1); }
|
||||
10% {
|
||||
-webkit-transform: scale(1.1);
|
||||
-moz-transform: scale(1.1);
|
||||
-ms-transform: scale(1.1);
|
||||
-o-transform: scale(1.1);
|
||||
transform: scale(1.1); }
|
||||
20% {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
-o-transform: scale(1);
|
||||
transform: scale(1); } }
|
||||
.dropzone, .dropzone * {
|
||||
box-sizing: border-box; }
|
||||
|
||||
.dropzone {
|
||||
min-height: 150px;
|
||||
border: 2px solid rgba(0, 0, 0, 0.3);
|
||||
background: white;
|
||||
padding: 20px 20px; }
|
||||
.dropzone.dz-clickable {
|
||||
cursor: pointer; }
|
||||
.dropzone.dz-clickable * {
|
||||
cursor: default; }
|
||||
.dropzone.dz-clickable .dz-message, .dropzone.dz-clickable .dz-message * {
|
||||
cursor: pointer; }
|
||||
.dropzone.dz-started .dz-message {
|
||||
display: none; }
|
||||
.dropzone.dz-drag-hover {
|
||||
border-style: solid; }
|
||||
.dropzone.dz-drag-hover .dz-message {
|
||||
opacity: 0.5; }
|
||||
.dropzone .dz-message {
|
||||
text-align: center;
|
||||
margin: 2em 0; }
|
||||
.dropzone .dz-preview {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin: 16px;
|
||||
min-height: 100px; }
|
||||
.dropzone .dz-preview:hover {
|
||||
z-index: 1000; }
|
||||
.dropzone .dz-preview:hover .dz-details {
|
||||
opacity: 1; }
|
||||
.dropzone .dz-preview.dz-file-preview .dz-image {
|
||||
border-radius: 20px;
|
||||
background: #999;
|
||||
background: linear-gradient(to bottom, #eee, #ddd); }
|
||||
.dropzone .dz-preview.dz-file-preview .dz-details {
|
||||
opacity: 1; }
|
||||
.dropzone .dz-preview.dz-image-preview {
|
||||
background: white; }
|
||||
.dropzone .dz-preview.dz-image-preview .dz-details {
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
-moz-transition: opacity 0.2s linear;
|
||||
-ms-transition: opacity 0.2s linear;
|
||||
-o-transition: opacity 0.2s linear;
|
||||
transition: opacity 0.2s linear; }
|
||||
.dropzone .dz-preview .dz-remove {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
border: none; }
|
||||
.dropzone .dz-preview .dz-remove:hover {
|
||||
text-decoration: underline; }
|
||||
.dropzone .dz-preview:hover .dz-details {
|
||||
opacity: 1; }
|
||||
.dropzone .dz-preview .dz-details {
|
||||
z-index: 20;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
font-size: 13px;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 2em 1em;
|
||||
text-align: center;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
line-height: 150%; }
|
||||
.dropzone .dz-preview .dz-details .dz-size {
|
||||
margin-bottom: 1em;
|
||||
font-size: 16px; }
|
||||
.dropzone .dz-preview .dz-details .dz-filename {
|
||||
white-space: nowrap; }
|
||||
.dropzone .dz-preview .dz-details .dz-filename:hover span {
|
||||
border: 1px solid rgba(200, 200, 200, 0.8);
|
||||
background-color: rgba(255, 255, 255, 0.8); }
|
||||
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis; }
|
||||
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {
|
||||
border: 1px solid transparent; }
|
||||
.dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span {
|
||||
background-color: rgba(255, 255, 255, 0.4);
|
||||
padding: 0 0.4em;
|
||||
border-radius: 3px; }
|
||||
.dropzone .dz-preview:hover .dz-image img {
|
||||
-webkit-transform: scale(1.05, 1.05);
|
||||
-moz-transform: scale(1.05, 1.05);
|
||||
-ms-transform: scale(1.05, 1.05);
|
||||
-o-transform: scale(1.05, 1.05);
|
||||
transform: scale(1.05, 1.05);
|
||||
-webkit-filter: blur(8px);
|
||||
filter: blur(8px); }
|
||||
.dropzone .dz-preview .dz-image {
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
position: relative;
|
||||
display: block;
|
||||
z-index: 10; }
|
||||
.dropzone .dz-preview .dz-image img {
|
||||
display: block; }
|
||||
.dropzone .dz-preview.dz-success .dz-success-mark {
|
||||
-webkit-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
-moz-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
-ms-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
-o-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); }
|
||||
.dropzone .dz-preview.dz-error .dz-error-mark {
|
||||
opacity: 1;
|
||||
-webkit-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
-moz-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
-ms-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
-o-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); }
|
||||
.dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
z-index: 500;
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -27px;
|
||||
margin-top: -27px; }
|
||||
.dropzone .dz-preview .dz-success-mark svg, .dropzone .dz-preview .dz-error-mark svg {
|
||||
display: block;
|
||||
width: 54px;
|
||||
height: 54px; }
|
||||
.dropzone .dz-preview.dz-processing .dz-progress {
|
||||
opacity: 1;
|
||||
-webkit-transition: all 0.2s linear;
|
||||
-moz-transition: all 0.2s linear;
|
||||
-ms-transition: all 0.2s linear;
|
||||
-o-transition: all 0.2s linear;
|
||||
transition: all 0.2s linear; }
|
||||
.dropzone .dz-preview.dz-complete .dz-progress {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.4s ease-in;
|
||||
-moz-transition: opacity 0.4s ease-in;
|
||||
-ms-transition: opacity 0.4s ease-in;
|
||||
-o-transition: opacity 0.4s ease-in;
|
||||
transition: opacity 0.4s ease-in; }
|
||||
.dropzone .dz-preview:not(.dz-processing) .dz-progress {
|
||||
-webkit-animation: pulse 6s ease infinite;
|
||||
-moz-animation: pulse 6s ease infinite;
|
||||
-ms-animation: pulse 6s ease infinite;
|
||||
-o-animation: pulse 6s ease infinite;
|
||||
animation: pulse 6s ease infinite; }
|
||||
.dropzone .dz-preview .dz-progress {
|
||||
opacity: 1;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
height: 16px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-top: -8px;
|
||||
width: 80px;
|
||||
margin-left: -40px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
-webkit-transform: scale(1);
|
||||
border-radius: 8px;
|
||||
overflow: hidden; }
|
||||
.dropzone .dz-preview .dz-progress .dz-upload {
|
||||
background: #333;
|
||||
background: linear-gradient(to bottom, #666, #444);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
-webkit-transition: width 300ms ease-in-out;
|
||||
-moz-transition: width 300ms ease-in-out;
|
||||
-ms-transition: width 300ms ease-in-out;
|
||||
-o-transition: width 300ms ease-in-out;
|
||||
transition: width 300ms ease-in-out; }
|
||||
.dropzone .dz-preview.dz-error .dz-error-message {
|
||||
display: block; }
|
||||
.dropzone .dz-preview.dz-error:hover .dz-error-message {
|
||||
opacity: 1;
|
||||
pointer-events: auto; }
|
||||
.dropzone .dz-preview .dz-error-message {
|
||||
pointer-events: none;
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
display: block;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.3s ease;
|
||||
-moz-transition: opacity 0.3s ease;
|
||||
-ms-transition: opacity 0.3s ease;
|
||||
-o-transition: opacity 0.3s ease;
|
||||
transition: opacity 0.3s ease;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
top: 130px;
|
||||
left: -10px;
|
||||
width: 140px;
|
||||
background: #be2626;
|
||||
background: linear-gradient(to bottom, #be2626, #a92222);
|
||||
padding: 0.5em 1.2em;
|
||||
color: white; }
|
||||
.dropzone .dz-preview .dz-error-message:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 64px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #be2626; }
|
2052
_examples/tutorial/dropzonejs/src/public/js/dropzone.js
Normal file
2052
_examples/tutorial/dropzonejs/src/public/js/dropzone.js
Normal file
File diff suppressed because it is too large
Load Diff
53
_examples/tutorial/dropzonejs/src/views/upload.html
Normal file
53
_examples/tutorial/dropzonejs/src/views/upload.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>DropzoneJS Uploader</title>
|
||||
|
||||
<!-- 1 -->
|
||||
<link href="/public/css/dropzone.css" type="text/css" rel="stylesheet" />
|
||||
|
||||
<!-- 2 -->
|
||||
<script src="/public/js/dropzone.js"></script>
|
||||
<!-- 4 -->
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
|
||||
<!-- 5 -->
|
||||
<script>
|
||||
Dropzone.options.myDropzone = {
|
||||
paramName: "file", // The name that will be used to transfer the file
|
||||
init: function () {
|
||||
thisDropzone = this;
|
||||
// 6
|
||||
$.get('/uploads', function (data) {
|
||||
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
// 7
|
||||
$.each(data, function (key, value) {
|
||||
var mockFile = { name: value.name, size: value.size };
|
||||
|
||||
thisDropzone.emit("addedfile", mockFile);
|
||||
thisDropzone.options.thumbnail.call(thisDropzone, mockFile, '/public/uploads/thumbnail_' + value.name);
|
||||
// thisDropzone.createThumbnailFromUrl(mockFile, '/public/uploads/' + value.name); <- doesn't work...
|
||||
// Make sure that there is no progress bar, etc...
|
||||
thisDropzone.emit("complete", mockFile);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- 3 -->
|
||||
<form action="/upload" method="POST" class="dropzone" id="my-dropzone">
|
||||
<div class="fallback">
|
||||
<input name="file" type="file" multiple />
|
||||
<input type="submit" value="Upload" />
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
|
||||
</html>
|
BIN
_examples/tutorial/dropzonejs/with_files.png
Normal file
BIN
_examples/tutorial/dropzonejs/with_files.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
Loading…
Reference in New Issue
Block a user