adminjs-upload icon indicating copy to clipboard operation
adminjs-upload copied to clipboard

Upload to AWS has the wrong ContentType

Open irenmax opened this issue 5 years ago • 4 comments

Hey there!

Describe the bug I am using the upload feature to upload images directly to S3. When uploading a file to AWS, the ContentType of an uploaded image to the edit page of a record is image/png but on S3, the ContentType is binary/octet-stream after the image upload to S3 is complete.

Did I miss setting an option for the uploadFeature in my project or is this a bug?

I managed to set the right ContentType for the upload by adding ContentType: file.type to the upload params inside the aws-provider.

Installed libraries and their versions

To Reproduce Steps to reproduce the behavior:

  1. Select AWS as provider for the upload feature
  2. Open the edit page of a record where the upload feature is enabled
  3. Upload an image
  4. Go to the S3 Bucket where the image was uploaded, select the uploaded image
  5. Open the Metadata information, check the ContentType

Expected behavior The ContentType of the file in S3 after the upload should be the same as the ContentType of the uploaded file. For example, if I upload a png image the ContentType of the file in S3 should be image/png.

AdminBroOptions with schema

resource: User,
                options: {
                    properties: {
                        fileUrl: {
                            isVisible: true,
                        },
                        mimeType: {
                            isVisible: true,
                        },
                    },
                },
                features: [uploadFeature({
                    provider: {
                        aws: {
                            region: `${process.env.AWS_REGION}`,
                            bucket: `${process.env.S3_IMAGE_BUCKET_NAME}`,
                            accessKeyId: `${process.env.AWS_USER_KEY}`,
                            secretAccessKey: `${process.env.AWS_USER_SECRET}`,
                            expires: 0,
                        },
                    },
                    properties: {
                        key: 'fileUrl',
                        mimeType: 'mimeType',
                    },
                    uploadPath: (record: BaseRecord, filename: string) => {
                        return `${record.get('id')}/${filename}`;
                    },
                })]

Additional context

I could fix the issue by changing the upload function in upload/src/features/upload-file/provider/aws-provider.ts from

public async upload(file: UploadedFile, key: string): Promise<S3.ManagedUpload.SendData> {
    const uploadOptions = { partSize: 5 * 1024 * 1024, queueSize: 10 }
    const tmpFile = fs.createReadStream(file.path)
    const params: S3.PutObjectRequest = {
      Bucket: this.bucket,
      Key: key,
      Body: tmpFile,
    }
    if (!this.expires) {
      params.ACL = 'public-read'
    }
    return this.s3.upload(params, uploadOptions).promise()
  }

to

public async upload(file: UploadedFile, key: string): Promise<S3.ManagedUpload.SendData> {
    const uploadOptions = { partSize: 5 * 1024 * 1024, queueSize: 10 }
    const tmpFile = fs.createReadStream(file.path)
    const params: S3.PutObjectRequest = {
      Bucket: this.bucket,
      Key: key,
      ContentType: file.type,
      Body: tmpFile,
    }
    if (!this.expires) {
      params.ACL = 'public-read'
    }
    return this.s3.upload(params, uploadOptions).promise()
  }

So I am not sure if the ContentType param is missing here of if I made a mistake setting up the upload feature.

irenmax avatar Nov 27 '20 09:11 irenmax

I had to create a fork and make the required changes in that. I have published that on npm for now. But I would like to discuss how to go about solving this issue right here in this repo. I am open to working on a PR.

Reference:

https://www.npmjs.com/package/neo-adminjs-upload npm i neo-adminjs-upload

https://github.com/adarshsingh1407/adminjs-upload

adarshsingh1407 avatar Jul 09 '22 18:07 adarshsingh1407

Just came across the same issue here. Thanks for the fix!

kyryl-bogach avatar Sep 13 '22 16:09 kyryl-bogach

If you want to keep using the original repo, there's an alternative:

Copy the aws-provider.ts into your own repository like:

import { UploadedFile } from 'adminjs';
import { S3 } from 'aws-sdk';
import fs from 'fs';
import { AWSOptions, BaseProvider } from '@adminjs/upload';

// Extending AWSProvider to fix issue https://github.com/SoftwareBrothers/adminjs-upload/issues/37

export class AWSProvider extends BaseProvider {
  private s3: S3

  public expires: number

  constructor(options: AWSOptions) {
    super(options.bucket)

    let AWS_S3: typeof S3
    try {
      // eslint-disable-next-line
      const AWS = require('aws-sdk')
      AWS_S3 = AWS.S3
    } catch (error) {
      throw new Error()
    }
    this.expires = options.expires ?? 86400
    this.s3 = new AWS_S3(options)
  }

  public async upload(file: UploadedFile, key: string): Promise<S3.ManagedUpload.SendData> {
    const uploadOptions = { partSize: 5 * 1024 * 1024, queueSize: 10 }
    const tmpFile = fs.createReadStream(file.path)
    const params: S3.PutObjectRequest = {
      Bucket: this.bucket,
      Key: key,
      Body: tmpFile,
      ContentType: file.type, // THIS IS THE CHANGE/FIX
    }
    if (!this.expires) {
      params.ACL = 'public-read'
    }
    return this.s3.upload(params, uploadOptions).promise()
  }

  public async delete(key: string, bucket: string): Promise<S3.DeleteObjectOutput> {
    return this.s3.deleteObject({ Key: key, Bucket: bucket }).promise()
  }

  public async path(key: string, bucket: string): Promise<string> {
    if (this.expires) {
      return this.s3.getSignedUrl('getObject', {
        Key: key,
        Bucket: bucket,
        Expires: this.expires,
      })
    }
    // https://bucket.s3.amazonaws.com/key
    return `https://${bucket}.s3.amazonaws.com/${key}`
  }
}

And use it like:

provider: new AWSProvider({
  region: '...',
  bucket: '...',
  accessKeyId: '...',
  secretAccessKey: '...'
}),

kyryl-bogach avatar Sep 14 '22 08:09 kyryl-bogach

Hi i changed the fix which is added the content type but still it is not working please can u help?

shub-create avatar May 03 '23 07:05 shub-create