Beispiel #1
0
// processExif attempts to rotate a JPEG based on the exif data. If the exif data
// cannot be decoded or the orientation tag not read, we return nil so that the image
// may continue to be uploaded. If there is an error encoding the image after
// modification, this is returned to the caller.
func (f *FileMetadataType) processExif() error {
	// Decode exif.
	ex, err := exif.Decode(bytes.NewReader(f.Content))
	if err != nil {
		return nil
	}
	// Get orientation tag.
	tag, err := ex.Get(exif.Orientation)
	if err != nil {
		return nil
	}
	orientation, err := tag.Int(0)
	if err != nil {
		return nil
	}

	var (
		angle            int
		flipMode         exifutil.FlipDirection
		switchDimensions bool
	)

	angle, flipMode, switchDimensions = exifutil.ProcessOrientation(int64(orientation))

	im, _, err := image.Decode(bytes.NewReader(f.Content))
	if err != nil {
		return err
	}

	if angle != 0 {
		im = exifutil.Rotate(im, angle)
	}

	if flipMode != 0 {
		im = exifutil.Flip(im, flipMode)
	}

	if switchDimensions {
		f.Width, f.Height = f.Height, f.Width
	}

	// Encode JPEG and replace f.Content.
	buf := new(bytes.Buffer)
	err = jpeg.Encode(buf, im, nil)
	if err != nil {
		return err
	}
	f.Content = buf.Bytes()

	// Update the hash and filesize based on changed content.
	sha1, err := h.SHA1(f.Content)
	if err != nil {
		return err
	}
	f.FileHash = sha1
	f.FileSize = int32(len(f.Content))

	return nil
}
Beispiel #2
0
// StoreGravatar stores the gravatar file in the database
func StoreGravatar(gravatarURL string) (FileMetadataType, int, error) {

	// TODO(matt): reduce duplication with models.FileController
	resp, err := http.Get(gravatarURL)

	if err != nil {
		glog.Errorf("http.Get(`%s`) %+v", gravatarURL, err)
		return FileMetadataType{}, http.StatusInternalServerError,
			fmt.Errorf("Could not retrieve gravatar")
	}
	defer resp.Body.Close()

	fileContent, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		glog.Errorf("ioutil.ReadAll(resp.Body) %+v", err)
		return FileMetadataType{}, http.StatusInternalServerError,
			fmt.Errorf("Could not read gravatar response")
	}

	metadata := FileMetadataType{}
	metadata.Content = fileContent
	metadata.FileSize = int32(len(metadata.Content))
	metadata.FileHash, err = h.SHA1(metadata.Content)
	if err != nil {
		glog.Errorf("h.Sha1(metadata.Content) %+v", err)
		return FileMetadataType{}, http.StatusInternalServerError,
			fmt.Errorf("Could not generate file SHA-1")
	}
	metadata.MimeType = resp.Header.Get("Content-Type")
	metadata.Created = time.Now()
	metadata.AttachCount++

	status, err := metadata.Insert(AvatarMaxWidth, AvatarMaxHeight)
	if err != nil {
		glog.Errorf("metadata.Insert(%d, %d) %+v", AvatarMaxWidth, AvatarMaxHeight, err)
		return FileMetadataType{}, status,
			fmt.Errorf("Could not insert gravatar file metadata")
	}

	return metadata, http.StatusOK, nil
}
Beispiel #3
0
// Create handles POST
func (ctl *FilesController) Create(c *models.Context) {
	if c.Auth.UserID < 1 {
		c.RespondWithErrorMessage(h.NoAuthMessage, http.StatusForbidden)
		return
	}

	mr, err := c.Request.MultipartReader()
	if err != nil {
		c.RespondWithErrorMessage(
			fmt.Sprintf("Only multipart forms can be posted: %v", err.Error()),
			http.StatusBadRequest,
		)
		return
	}

	var files []models.FileMetadataType

	part, err := mr.NextPart()
	for err == nil {
		// FormName() is only populated if the part has
		// Content-Disposition set to "form-data"
		if part.FormName() != "" {
			if part.FileName() != "" {

				// Persist file and metadata
				md := models.FileMetadataType{}
				md.FileName = part.FileName()
				md.Created = time.Now()
				md.MimeType = part.Header.Get("Content-Type")

				md.Content, err = ioutil.ReadAll(part)
				if err != nil {
					c.RespondWithErrorMessage(
						fmt.Sprintf("Couldn't not read form part: %v", err.Error()),
						http.StatusBadRequest,
					)
					return
				}

				sha1, err := h.SHA1(md.Content)
				if err != nil {
					c.RespondWithErrorMessage(
						fmt.Sprintf("Couldn't generate SHA-1: %v", err.Error()),
						http.StatusInternalServerError,
					)
					return
				}
				md.FileHash = sha1
				md.FileSize = int32(len(md.Content))

				// Check whether the file size overflowed int32 (over 2GB)
				if int(md.FileSize) != len(md.Content) {
					c.RespondWithErrorMessage(
						"File too large. Max size = 2147482548 bytes (2GB)",
						http.StatusInternalServerError,
					)
					return
				}

				// Resize if needed
				query := c.Request.URL.Query()

				var (
					maxWidth  int64
					maxHeight int64
				)
				if query.Get("maxWidth") != "" {
					max, err := strconv.ParseInt(strings.Trim(query.Get("maxWidth"), " "), 10, 64)
					if err != nil || max < 0 {
						c.RespondWithErrorMessage("maxWidth needs to be a positive integer", http.StatusBadRequest)
						return
					}
					maxWidth = max
				}
				if query.Get("maxHeight") != "" {
					max, err := strconv.ParseInt(strings.Trim(query.Get("maxHeight"), " "), 10, 64)
					if err != nil || max < 0 {
						c.RespondWithErrorMessage("maxHeight needs to be a positive integer", http.StatusBadRequest)
						return
					}
					maxHeight = max
				}

				status, err := md.Insert(maxWidth, maxHeight)
				if err != nil {
					c.RespondWithErrorMessage(
						fmt.Sprintf("Couldn't upload file and metadata: %v", err.Error()),
						status,
					)
					return
				}
				files = append(files, md)
			}
		}
		part, err = mr.NextPart()
	}

	c.RespondWithData(files)
}
Beispiel #4
0
// ResizeImage will resize an image (usually an avatar) to fit within the given
// constraints whilst preserving the aspect ratio
func (f *FileMetadataType) ResizeImage(
	maxWidth int64,
	maxHeight int64,
) (
	int,
	error,
) {
	var (
		width  int
		height int
	)

	if maxWidth > 0 && f.Width > maxWidth {
		width = int(maxWidth)
	}

	if maxHeight > 0 && f.Height > maxHeight && f.Height > f.Width {
		width = 0
		height = int(maxHeight)
	}

	if width == 0 && height == 0 {
		// Nothing to do, either the params weren't supplied or the image is
		// already small enough
		return http.StatusOK, nil
	}

	r := bytes.NewReader(f.Content)

	// middle var is format, i.e. which decoder was used: "gif", "jpeg", "png"
	// in the case of "gif", only the first frame is extracted
	img, format, err := image.Decode(r)
	if err != nil {
		glog.Errorf("image.Decode(r) %+v", err)
		return http.StatusBadRequest, err
	}

	m := imaging.Resize(img, width, height, imaging.Lanczos)

	var buf bytes.Buffer

	switch format {
	case "gif":
		err = gif.Encode(&buf, m, nil)
		if err != nil {
			glog.Errorf("gif.Encode(&buf, m, nil) %+v", err)
			return http.StatusBadRequest, err
		}
		f.MimeType = ImageGifMimeType
	case "jpeg":
		err = jpeg.Encode(&buf, m, nil)
		if err != nil {
			glog.Errorf("jpeg.Encode(&buf, m, nil) %+v", err)
			return http.StatusBadRequest, err
		}
		f.MimeType = ImageJpegMimeType
	default:
		err = png.Encode(&buf, m)
		if err != nil {
			glog.Errorf("png.Encode(&buf, m, nil) %+v", err)
			return http.StatusBadRequest, err
		}
		f.MimeType = ImagePngMimeType
	}

	// Update the file meta data
	f.Content = buf.Bytes()

	sha1, err := h.SHA1(f.Content)
	if err != nil {
		glog.Errorf("h.Sha1(f.Content) %+v", err)
		return http.StatusInternalServerError,
			fmt.Errorf("Couldn't generate SHA-1")
	}
	f.FileHash = sha1
	f.FileSize = int32(len(f.Content))

	im, _, err := image.DecodeConfig(bytes.NewReader(f.Content))
	if err != nil {
		glog.Errorf(
			"image.DecodeConfig(bytes.NewReader(f.Content)) %+v",
			err,
		)
		return http.StatusInternalServerError, err
	}
	f.Height = int64(im.Height)
	f.Width = int64(im.Width)

	return http.StatusOK, nil
}