56 Star 154 Fork 40

青苗 / gmfs

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
gridfs.go 15.44 KB
一键复制 编辑 原始数据 按行查看 历史
= 提交于 2015-08-28 18:16 . 支持 gif 图片动态播放
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
package storage
import (
"bytes"
"errors"
"fmt"
"gmfs/core/logger"
"gmfs/core/tools/pic"
"gmfs/core/tools/pic/nude"
"gmfs/core/tools/pic/resize"
"gmfs/core/tools/util"
"gmfs/core/web/ink"
"gmfs/core/web/model"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"image"
"image/color/palette"
"image/draw"
"image/gif"
"image/png"
"io"
"os"
"time"
)
const db_name = "gmfs"
/**
* image gridfs
*/
func imageGridfs(session *mgo.Session) *mgo.GridFS {
return session.DB(db_name).GridFS("img")
}
/**
* file gridfs
*/
func fileGridfs(session *mgo.Session) *mgo.GridFS {
return session.DB(db_name).GridFS("file")
}
/**
* Collection
*/
func getCollection(session *mgo.Session, name string) *mgo.Collection {
return session.DB(db_name).C(name)
}
/**
* 文件资源
*/
func getRes(session *mgo.Session) *mgo.Collection {
return getCollection(session, "res")
}
/**
* 文件信息
*/
func getInfo(session *mgo.Session) *mgo.Collection {
return getCollection(session, "info")
}
/**
* 文件黑名单
*/
func getBlacklist(session *mgo.Session) *mgo.Collection {
return getCollection(session, "blacklist")
}
/**
* 转换静态图片
*/
func ResizeImage(ctx *ink.Context, gf *mgo.GridFile, b bool, w, h uint) *bytes.Buffer {
var buf = new(bytes.Buffer)
var use = useWatermark(ctx, w, h)
img, name, _ := pic.DecodeImage(gf)
//true 处理图片,false 不处理原样输出
if b {
dst := resize.Resize(w, h, img, resize.Lanczos3)
//判断是否添加水印
if use {
m, err := addWatermark(ctx, dst)
if err != nil {
logger.Errorln(err)
} else {
//返回水印图片
pic.Encode(buf, m, name)
return buf
}
}
pic.Encode(buf, dst, name)
} else {
//判断是否添加水印
if use {
m, err := addWatermark(ctx, img)
if err != nil {
logger.Errorln(err)
} else {
//返回水印图片
pic.Encode(buf, m, name)
return buf
}
}
pic.Encode(buf, img, name)
}
return buf
}
/**
* 转换GIF图片
*/
func ResizeGif(ctx *ink.Context, r io.Reader, rs bool, width, hight uint) *bytes.Buffer {
var buf = new(bytes.Buffer)
var use = useWatermark(ctx, width, hight)
// Decode the original gif.
im, err := gif.DecodeAll(r)
if err != nil {
return nil
}
// Create a new RGBA image to hold the incremental frames.
firstFrame := im.Image[0].Bounds()
b := image.Rect(0, 0, firstFrame.Dx(), firstFrame.Dy())
img := image.NewRGBA(b)
// Resize each frame.
for index, frame := range im.Image {
bounds := frame.Bounds()
draw.Draw(img, bounds, frame, bounds.Min, draw.Over)
//是否处理图片
if rs {
//是否添加水印
if use {
wmimg, _ := addWatermark(ctx, img)
im.Image[index] = ImageToPaletted(ProcessImage(wmimg, width, hight))
} else {
im.Image[index] = ImageToPaletted(ProcessImage(img, width, hight))
}
} else {
//是否添加水印
if use {
wmimg, _ := addWatermark(ctx, img)
im.Image[index] = ImageToPaletted(wmimg)
} else {
im.Image[index] = ImageToPaletted(img)
}
}
}
gif.EncodeAll(buf, im)
return buf
}
/**
* 执行图片剪切
*/
func ProcessImage(img image.Image, width, hight uint) image.Image {
return resize.Resize(width, hight, img, resize.NearestNeighbor)
}
func ImageToPaletted(img image.Image) *image.Paletted {
b := img.Bounds()
pm := image.NewPaletted(b, palette.Plan9)
draw.FloydSteinberg.Draw(pm, b, img, image.ZP)
return pm
}
/**
* 添加水印
*/
func addWatermark(ctx *ink.Context, img image.Image) (*image.NRGBA, error) {
var wp = ctx.App().Config().String("img.watermark_path")
wmb, _ := os.Open(wp)
watermark, err := png.Decode(wmb)
if err != nil {
return nil, err
}
defer wmb.Close()
//把水印写到右下角,并向0坐标各偏移10个像素
offset := image.Pt(img.Bounds().Dx()-watermark.Bounds().Dx()-10, img.Bounds().Dy()-watermark.Bounds().Dy()-10)
b := img.Bounds()
m := image.NewNRGBA(b)
draw.Draw(m, b, img, image.ZP, draw.Src)
draw.Draw(m, watermark.Bounds().Add(offset), watermark, image.ZP, draw.Over)
return m, nil
}
/**
* 判断是否需要加水印
*/
func useWatermark(ctx *ink.Context, w, h uint) bool {
var rlt = false
var use = ctx.App().Config().Bool("img.watermark_use")
if !use {
//全局设置不开启水印,请求可通过该参数打开水印
watermark := ctx.Input()["watermark"]
if watermark == "true" {
use = true
}
}
if use {
width := ctx.App().Config().Int("img.watermark_width")
height := ctx.App().Config().Int("img.watermark_height")
//w,h 为0 原图加水印,w不为0 h为 0 等比缩放
if w >= uint(width) || w == 0 {
if h == 0 || h >= uint(height) {
rlt = true
}
}
}
return rlt
}
/**
* 根据 md5,length
* 查询文件是否存在黑名单中
*/
func ExistBlacklist(session *mgo.Session, fileMd5 string, fileSize int) (*model.Blacklist, error) {
var result *model.Blacklist
err := getBlacklist(session).Find(bson.M{"md5": fileMd5, "size": fileSize}).One(&result)
if err != nil {
return nil, err
}
logger.Debugf(" search ImageBlacklistExist : %v", result)
return result, nil
}
/**
* 根据 id
* 查询文件
*/
func FindInfoById(session *mgo.Session, id string) (*model.Info, *mgo.GridFile, error) {
isId := bson.IsObjectIdHex(id)
if !isId {
return nil, nil, errors.New("id error")
}
inf, err := queryInfoById(session, id)
if err != nil {
return nil, nil, err
}
gf, err := queryGridFile(session, inf.GetFileType(), bson.M{"_id": bson.ObjectIdHex(inf.GetFid())})
return inf, gf, err
}
/**
* 根据 id
* 查询文件信息
*/
func queryInfoById(session *mgo.Session, id string) (*model.Info, error) {
var inf *model.Info
err := getInfo(session).FindId(bson.ObjectIdHex(id)).One(&inf)
if err != nil {
return nil, err
}
return inf, nil
}
/**
* 根据 Fid
* 查询文件资源
*/
func queryResByFid(session *mgo.Session, fid string) (*model.Res, error) {
var res *model.Res
err := getRes(session).Find(bson.M{"fid": fid}).One(&res)
if err != nil {
return nil, err
}
return res, nil
}
/**
* 查询图片
*/
func queryGridFile(session *mgo.Session, fileType int, query bson.M) (*mgo.GridFile, error) {
var fp *mgo.GridFile
var gfs *mgo.GridFS
if fileType == 0 {
//0、图片文件
gfs = imageGridfs(session)
} else {
//1、二进制文件
gfs = fileGridfs(session)
}
iter := gfs.Find(query).Iter()
gfs.OpenNext(iter, &fp)
if fp == nil {
return fp, fmt.Errorf("no image found for %s", query)
}
return fp, nil
}
/**
* 删除 gridfs 图片文件
*/
func removeImgGridfs(session *mgo.Session, id string) error {
return imageGridfs(session).RemoveId(bson.ObjectIdHex(id))
}
/**
* 删除图片资源文件
*/
func removeImgRes(session *mgo.Session, resId bson.ObjectId, fid string) error {
err := getRes(session).RemoveId(resId)
if err != nil {
return err
}
return imageGridfs(session).RemoveId(bson.ObjectIdHex(fid))
}
/**
* 根据 md5,length
* 查询文件是否存在
*/
func resExist(session *mgo.Session, fileMd5 string, fileSize int) (*model.Res, error) {
var result *model.Res
err := getRes(session).Find(bson.M{"md5": fileMd5, "size": fileSize}).One(&result)
if err != nil {
return nil, err
}
return result, nil
}
/**
* 秒传根据 md5,length
* 查询文件信息
*/
func SearchExist(session *mgo.Session, fileMd5 string, fileSize int) (*model.Info, error) {
res, err := resExist(session, fileMd5, fileSize)
if err != nil {
logger.Errorf(" SearchExist : %v", err)
return nil, err
}
inf, err := saveInfo(session, res)
if err != nil {
logger.Errorf(" SearchExist saveInfo: %v", err)
return nil, err
}
return inf, nil
}
/**
* 保存文件资源
*/
func saveRes(ctx *ink.Context, session *mgo.Session, res *model.Res) (string, string, error) {
id := bson.NewObjectId()
err := getRes(session).Insert(&model.Res{id, res.GetFid(), res.GetMd5(), res.GetSize(),
res.GetRefnum(), res.GetNude(), res.GetOnline(), res.GetFileType(),
res.GetFileSuffix(), res.GetContentType(), res.GetUploadDate()})
if err != nil {
logger.Errorf(" saveRes error: ", err)
return "", "", err
}
//重置图片处理
if res.GetFileType() == 0 {
rif, _ := resetImgInfo(ctx, session, res)
if rif != nil {
return rif.GetFid(), rif.GetRdm(), nil
}
}
//正常上传
inf, err := saveInfo(session, res)
if err != nil {
logger.Errorf(" saveRes saveInfo error: ", err)
return "", "", err
}
return inf.GetIdToString(), inf.GetRdm(), nil
}
/**
* 替换图片文件
* 参数: resetId 重置ID , resetRdm 重置随机数
*/
func resetImgInfo(ctx *ink.Context, session *mgo.Session, res *model.Res) (*model.Info, error) {
resetId := ctx.Input()["resetId"]
resetRdm := ctx.Input()["resetRdm"]
if resetId != "" && resetRdm != "" {
//查询当前关联的文件资源
inf, err := queryInfoById(session, resetId)
if err != nil {
return nil, err
}
rf, err := queryResByFid(session, inf.GetFid())
if err != nil {
return nil, err
}
//文件资源是否被引用,是 -1 否 删除
if rf.GetRefnum() >= 1 {
updateResRefnum(session, rf, false)
} else {
removeImgRes(session, rf.GetId(), rf.GetFid())
}
//更新关联资源信息
i := model.NewInfoByRes(res)
err = getInfo(session).Update(bson.M{"_id": bson.ObjectIdHex(resetId), "rdm": resetRdm},
bson.M{"$set": bson.M{"fid": i.GetFid(), "nude": i.GetNude(), "online": i.GetOnline(),
"filetype": i.GetFileType(), "filesuffix": i.GetFileSuffix(), "contenttype": i.GetContentType(),
"uploaddate": i.GetUploadDate()}})
if err != nil {
logger.Errorf(" resetImgInfo error: ", err)
return nil, err
}
//ID保存不变
i.SetFid(resetId)
return i, nil
}
return nil, nil
}
/**
* 保存文件信息
*/
func saveInfo(session *mgo.Session, res *model.Res) (*model.Info, error) {
i := model.NewInfoByRes(res)
err := getInfo(session).Insert(&model.Info{i.GetId(), i.GetFid(), i.GetRdm(),
i.GetNude(), i.GetOnline(), i.GetFileType(), i.GetFileSuffix(),
i.GetContentType(), i.GetUploadDate()})
if err != nil {
logger.Errorf(" saveImginfo error: ", err)
return nil, err
}
return i, nil
}
/**
* 更新文件资源,引用次数
* 当上传的 md5 size 值一样时更新该参数,生成新的引用信息
*/
func updateResRefnum(session *mgo.Session, res *model.Res, add bool) (string, string, error) {
var refnum = res.GetRefnum()
if add {
refnum = refnum + 1
} else {
refnum = refnum - 1
}
err := getRes(session).UpdateId(res.GetId(), bson.M{"$set": bson.M{"refnum": refnum}})
if err != nil {
logger.Errorf(" updateResRefnum error: ", err)
return "", "", err
}
if !add {
//重置或删除图片,减去引用次数,直接返回
return "", "", nil
}
i, err := saveInfo(session, res)
if err != nil {
logger.Errorf(" updateResRefnum saveInfo error: ", err)
return "", "", err
}
return i.GetIdToString(), i.GetRdm(), nil
}
/**
* 文件转正
*/
func UpdateInfoOnline(session *mgo.Session, id, rdm string) error {
err := getInfo(session).Update(bson.M{"_id": bson.ObjectIdHex(id), "rdm": rdm}, bson.M{"$set": bson.M{"online": true}})
if err != nil {
return err
}
inf, err := queryInfoById(session, id)
if err != nil {
return err
}
return getRes(session).Update(bson.M{"fid": inf.GetFid()}, bson.M{"$set": bson.M{"online": true}})
}
/**
* 保存图片
*/
func SaveImage(ctx *ink.Context, session *mgo.Session, data []byte) (string, string, bool, error) {
var uploadImg image.Image
//之前是否上传
res := model.NewRes(data)
rf, err := resExist(session, res.GetMd5(), res.GetSize())
if err != nil && err.Error() != "not found" {
return "", "", false, err
}
//是否属于黑名单
ibl, err := ExistBlacklist(session, res.GetMd5(), res.GetSize())
if ibl != nil {
return "photo in blacklist", "", true, err
}
//上传图片
img, suffix, err := pic.DecodeImage(bytes.NewBuffer(data))
if err != nil {
return "", "", false, err
}
//鉴黄
isNude, err := nude.IsImageNude(img)
res.SetNude(isNude)
nudeUpload := ctx.App().Config().Bool("img.nude_upload")
if !nudeUpload {
//不允许上传
if err != nil {
return "", "", false, err
}
if isNude {
return "photo may be naked.", "", true, nil
}
} else {
//用户要求涉黄验证
nude := ctx.Input()["nude"]
if nude == "true" {
//不允许上传
if err != nil {
return "", "", false, err
}
if isNude {
return "photo may be naked.", "", true, nil
}
}
}
//已存在,引用+1 生成新的引用信息
if rf != nil {
fid, rdm, err := updateResRefnum(session, rf, true)
return fid, rdm, rf.GetNude(), err
}
//图片处理
paramIc := ctx.Input()["ic"]
if bson.IsObjectIdHex(paramIc) {
//暂时不处理该逻辑
// gift := giftImageClassify(session, paramIc)
// if gift != nil {
// dst := image.NewRGBA(gift.Bounds(img.Bounds()))
// gift.Draw(dst, img)
// //设置 md5 size
// var buf = new(bytes.Buffer)
// pic.Encode(buf, dst, suffix)
// ii.SetMd5(util.ByteMD5(buf.Bytes()))
// ii.SetSize(len(buf.Bytes()))
// uploadImg = dst
// }
uploadImg = img
} else {
uploadImg = img
}
//上传 gridfs
tgf, err := imageGridfs(session).Create(util.GenerateRandomFilename(true, suffix))
defer tgf.Close()
if err != nil {
return "", "", false, err
}
var fid = bson.NewObjectId()
tgf.SetId(fid)
tgf.SetContentType(fmt.Sprintf("image/%s", suffix))
//add Meta
width := uploadImg.Bounds().Dx()
height := uploadImg.Bounds().Dy()
metadata := bson.M{"width": width, "height": height}
tgf.SetMeta(metadata)
if suffix == "gif" {
_, err = io.Copy(tgf, bytes.NewReader(data))
if err != nil {
return "", "", false, err
}
} else {
var buf = new(bytes.Buffer)
pic.Encode(buf, uploadImg, suffix)
_, err = io.Copy(tgf, bytes.NewReader(buf.Bytes()))
if err != nil {
return "", "", false, err
}
}
//save res
res.SetOnline(fileOnline(ctx))
res.SetFid(fid.Hex())
res.SetFileType(0)
res.SetFileSuffix(fmt.Sprintf(".%s", suffix))
res.SetContentType(tgf.ContentType())
res.SetUploadDate(time.Now())
fileId, rdm, err := saveRes(ctx, session, res)
if err != nil {
return "", "", false, err
}
return fileId, rdm, isNude, nil
}
/**
* 保存文件
*/
func SaveFile(ctx *ink.Context, session *mgo.Session, data []byte, fileSuffix, filetype string) (string, string, error) {
//之前是否上传
res := model.NewRes(data)
rf, err := resExist(session, res.GetMd5(), res.GetSize())
if err != nil && err.Error() != "not found" {
return "", "", err
}
//是否属于黑名单
ibl, err := ExistBlacklist(session, res.GetMd5(), res.GetSize())
if ibl != nil {
return "file in blacklist", "", err
}
//已存在,引用+1 生成新的引用信息
if rf != nil {
return updateResRefnum(session, rf, true)
}
//上传 gridfs
tgf, err := fileGridfs(session).Create(util.GenerateRandomFilename(false, fileSuffix))
defer tgf.Close()
if err != nil {
return "", "", err
}
var fid = bson.NewObjectId()
tgf.SetId(fid)
tgf.SetContentType(filetype)
_, err = io.Copy(tgf, bytes.NewReader(data))
if err != nil {
return "", "", err
}
//save info
res.SetFid(fid.Hex())
res.SetNude(false)
res.SetOnline(fileOnline(ctx))
res.SetMd5(util.ByteMD5(data))
res.SetSize(len(data))
res.SetFileType(1)
res.SetFileSuffix(fileSuffix)
res.SetContentType(tgf.ContentType())
res.SetUploadDate(time.Now())
fileId, rdm, err := saveRes(ctx, session, res)
if err != nil {
return "", "", err
}
return fileId, rdm, nil
}
/**
* 全局允许上线 online 为 false 不允许上线才生效
*/
func fileOnline(ctx *ink.Context) bool {
var ol = true
fo := ctx.App().Config().Bool("app.file_online")
if fo {
online := ctx.Input()["online"]
if online == "false" {
ol = false
}
} else {
ol = false
}
return ol
}
Go
1
https://gitee.com/jobob/gmfs.git
git@gitee.com:jobob/gmfs.git
jobob
gmfs
gmfs
8375e82f13e4

搜索帮助