代码拉取完成,页面将自动刷新
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
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。