package main import ( "bytes" "context" "errors" "fmt" "io" "strconv" "image" "image/jpeg" _ "image/png" "mime/multipart" "net/http" "strings" "github.com/go-ldap/ldap/v3" "github.com/google/uuid" "github.com/gorilla/mux" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "github.com/nfnt/resize" ) //Upload image through guichet server. func uploadImage(w http.ResponseWriter, r *http.Request, login *LoginStatus) (bool, string, error) { file, _, err := r.FormFile("image") if err == http.ErrMissingFile { return false, "", nil } if err != nil { return false, "", err } defer file.Close() fileType, err := checkImage(file) if err != nil { return false, "", err } if fileType == "" { return false, "", nil } buff := bytes.NewBuffer([]byte{}) buff_thumbnail := bytes.NewBuffer([]byte{}) err = resizeThumb(file, buff, buff_thumbnail) if err != nil { return false, "", err } mc, err := newMimioClient() if err != nil { return false, "", err } if mc == nil { return false, "", err } var name, nameFull string if nameConsul := login.UserEntry.GetAttributeValue("profilImage"); nameConsul != "" { name = nameConsul nameFull = "full_" + name } else { name = uuid.New().String() + ".jpeg" nameFull = "full_" + name } _, err = mc.PutObject(context.Background(), "bottin-pictures", name, buff_thumbnail, int64(buff_thumbnail.Len()), minio.PutObjectOptions{ ContentType: "image/jpeg", }) if err != nil { return false, "", err } _, err = mc.PutObject(context.Background(), "bottin-pictures", nameFull, buff, int64(buff.Len()), minio.PutObjectOptions{ ContentType: "image/jpeg", }) if err != nil { return false, "", err } return true, name, nil } func newMimioClient() (*minio.Client, error) { endpoint := config.Endpoint accessKeyID := config.AccesKey secretKeyID := config.SecretKey useSSL := true //Initialize Minio minioCLient, err := minio.New(endpoint, &minio.Options{ Creds: credentials.NewStaticV4(accessKeyID, secretKeyID, ""), Secure: useSSL, Region: "garage", }) if err != nil { return nil, err } return minioCLient, nil } func checkImage(file multipart.File) (string, error) { buff := make([]byte, 512) //Detect read only the first 512 bytes _, err := file.Read(buff) if err != nil { return "", err } file.Seek(0, 0) fileType := http.DetectContentType(buff) fileType = strings.Split(fileType, "/")[0] switch fileType { case "image": return fileType, nil default: return "", errors.New("bad type") } } func resizeThumb(file multipart.File, buff, buff_thumbnail *bytes.Buffer) error { file.Seek(0, 0) images, _, err := image.Decode(file) if err != nil { return errors.New("Decode: " + err.Error()) } //Encode image to jpeg a first time to eliminate all problems err = jpeg.Encode(buff, images, &jpeg.Options{ Quality: 100, //Between 1 to 100, higher is better }) if err != nil { return err } images, _, err = image.Decode(buff) if err != nil { return err } buff.Reset() images = resize.Thumbnail(200, 200, images, resize.Lanczos3) images_thumbnail := resize.Thumbnail(80, 80, images, resize.Lanczos3) err = jpeg.Encode(buff, images, &jpeg.Options{ Quality: 95, }) if err != nil { return err } err = jpeg.Encode(buff_thumbnail, images_thumbnail, &jpeg.Options{ Quality: 95, }) return err } func handleDownloadImage(w http.ResponseWriter, r *http.Request) { //Get input value by user dn := mux.Vars(r)["name"] size := mux.Vars(r)["size"] //Check login login := checkLogin(w, r) if login == nil { return } var imageName string if dn != "unknown_profile" { //Search values with ldap and filter searchRequest := ldap.NewSearchRequest( dn, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, "(objectclass=*)", []string{"profilImage"}, nil) sr, err := login.conn.Search(searchRequest) if err != nil { http.Error(w, "Search: "+err.Error(), http.StatusInternalServerError) return } if len(sr.Entries) != 1 { http.Error(w, fmt.Sprintf("Not found user: %s cn: %s and numberEntries: %d", dn, strings.Split(dn, ",")[0], len(sr.Entries)), http.StatusInternalServerError) return } imageName = sr.Entries[0].GetAttributeValue("profilImage") if imageName == "" { http.Error(w, "User doesn't have profile image", http.StatusInternalServerError) return } } else { imageName = "unknown_profile.jpg" } if size == "full" { imageName = "full_" + imageName } //Get the object after connect MC mc, err := newMimioClient() if err != nil { http.Error(w, "MinioClient: "+err.Error(), http.StatusInternalServerError) return } obj, err := mc.GetObject(context.Background(), "bottin-pictures", imageName, minio.GetObjectOptions{}) if err != nil { http.Error(w, "MinioClient: GetObject: "+err.Error(), http.StatusInternalServerError) return } defer obj.Close() objStat, err := obj.Stat() if err != nil { http.Error(w, "MinioObjet: "+err.Error(), http.StatusInternalServerError) return } //Send JSON through xhttp w.Header().Set("Content-Type", objStat.ContentType) w.Header().Set("Content-Length", strconv.Itoa(int(objStat.Size))) //http.Error(w, fmt.Sprintf("Length buffer: %d", objStat.Size), http.StatusInternalServerError) buff := make([]byte, objStat.Size) obj.Seek(0, 0) n, err := obj.Read(buff) if err != nil && err != io.EOF { http.Error(w, fmt.Sprintf("Read Error: %s, bytes Read: %d, bytes in file: %d", err.Error(), n, objStat.Size), http.StatusInternalServerError) return } if int64(n) != objStat.Size { http.Error(w, fmt.Sprintf("Read %d bytes on %d bytes", n, objStat.Size), http.StatusInternalServerError) return } if _, err := w.Write(buff); err != nil { http.Error(w, "WriteBody: "+err.Error(), http.StatusInternalServerError) return } }