拖拽传图 Iris + DropzoneJS

Iris官方其实很厚道,有很多有用的例子,而社区则是文档多于实战。源于官方示例库([英文源文])(https://github.com/kataras/iris/tree/master/_examples/tutorial/dropzonejs),本文采用 DropzoneJS and Go实现图片上传,后端裁剪,前端呈现,代码量少但胜在实用,适合新同学实战。

涉及的点

  • DropzoneJS 拖拽上传图片前端库,可显示进度
  • Iris 类(理念相近)PHP 中的 Laravel 框架,但比之轻
  • 相关同步,图片,文件,路径等标准库
  • 第三方库 nfnt/resize,用于缩略图等比例裁剪

效果图

拖拽上传图片 DropzoneJS+Iris

前端

引入的DropzoneJS库本身不依赖于jQeury,表单亦无需 enctype,本文只是用jquery向后端发数据

DOM布局

表单 file 类型节点, 要携带 multiple属性,以示多文件上传

    <form action="/upload" method="post" class="dropzone" id="my-dropzone">
        <div class="fallback">
            <input type="file" name="file" multiple/>
            <input type="submit" value="Upload">
        </div>
    </form>

DropzoneJS

addedfile 事件,使用回调数据,其添加到对应的文件对象上去; complete 事件,显示进度条

    Dropzone.options.myDropzone ={
        paramName:"file",
        init:function(){
            thisDropzone = this;
            $.get('/uploads',function(data){
                if(data==null){
                    return
                }
                $.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.emit("complete", mockFile)
                })
            })
        }
    }

后端

项目目录结构如下

│  main.go
├─public
│  ├─css
│  │      dropzone.css
│  ├─js
│  │      dropzone.js
│  └─uploads
│          kk048.jpg
│          ll031.jpg
│          thumbnail_kk048.jpg
│          thumbnail_ll031.jpg
│          thumbnail_web_spring.png
│          web_spring.png
└─views
        upload.html

数据

文件标签结构体方便前端接收json格式的文件名及大小的信息

const uploadsDir = "./public/uploads/"
type uploadedFile struct {
	// {name: "", size: } 适应dropzone所需
	Name string `json:"name"`
	Size int64  `json:"size"`
}
type uploadedFiles struct {
	dir   string  // 文件存储目录
	items []uploadedFile // 多文件数据记录进切片
	mu    sync.RWMutex // 安全类型的切片锁
}

操作集

*uploadedFiles类型的创建及其方法

// 获取多文件实例
func scanUploads(dir string) *uploadedFiles {
	f := new(uploadedFiles)
	index := dir[len(dir)-1]
	if index != os.PathSeparator && index != '/' {
		dir += string(os.PathSeparator)
	}
	// 递归创建文件夹
	if err := os.MkdirAll(dir, os.FileMode(0666)); err != nil {
		return f
	}
	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 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 {
	uf := uploadedFile{
		Name: name,
		Size: size,
	}
	f.mu.Lock()
	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") {
		img, err := jpeg.Decode(file)
		if err != nil {
			return
		}
		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
		}
		// 等比例缩放
		resized := resize.Thumbnail(180, 180, img, resize.Lanczos3)
		png.Encode(out, resized)
	}
}

Iris应用

好的代码会说话,上码

func main() {
	app := iris.New()
	// 注册视图引擎
	app.RegisterView(iris.HTML("./views", ".html"))
	// 发布静态资源服务
	app.HandleDir("/public", "./public")
	// 注册首页路由
	app.Get("/", func(ctx iris.Context) {
		ctx.View("upload.html")
	})

	// 扫描上传后文件信息(缩略图信息)
	files := scanUploads(uploadsDir)
	// 多图片信息api查询接口,返回json
	app.Get("/uploads", func(ctx iris.Context) {
		ctx.JSON(files.items)
	})

	// 注册Post上传
	app.Post("/upload", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) {
		// 多文件上传 ctx是一个集请求与响应的组合体
		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

		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)

		// 可选 顺序添加文件列表
		uploadedFile := files.add(fname, info.Size)
		// goroutine创建缩略图
		go files.createThumbnail(uploadedFile)
	})
	app.Run(iris.Addr("localhost:8080"))
}

参考

源代码
Iris新手指北

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值