为啥上传到 S3 的文件的内容类型为 application/octet-stream,除非我将文件命名为 .html?

Posted

技术标签:

【中文标题】为啥上传到 S3 的文件的内容类型为 application/octet-stream,除非我将文件命名为 .html?【英文标题】:Why does file uploaded to S3 have content type application/octet-stream unless I name the file .html?为什么上传到 S3 的文件的内容类型为 application/octet-stream,除非我将文件命名为 .html? 【发布时间】:2015-08-22 17:55:20 【问题描述】:

即使我将内容类型设置为 text/html,它最终在 S3 上仍为 application/octet-stream。

ByteArrayInputStream contentsAsStream = new ByteArrayInputStream(contentAsBytes);
ObjectMetadata md = new ObjectMetadata();
md.setContentLength(contentAsBytes.length);
md.setContentType("text/html");
s3.putObject(new PutObjectRequest(ARTIST_BUCKET_NAME, artistId, contentsAsStream, md));
           

但是,如果我将文件命名为以 .html 结尾

s3.putObject(new PutObjectRequest(ARTIST_BUCKET_NAME, artistId + ".html", contentsAsStream, md));

然后就可以了。

我的 md 对象只是被忽略了吗?我如何以编程方式解决这个问题,因为随着时间的推移,我需要上传数千个文件,所以不能只进入 S3 UI 并手动修复 contentType。

【问题讨论】:

如何检查内容类型?在 AWS S3 UI 中还是通过 API?如果通过 API 可以粘贴代码。您是否使用最新版本的 AWS 开发工具包?如果是这样,你可以尝试一个。 我通过在 S3 控制台内的网络浏览器中打开查看内容类型来检查内容类型 我使用的是我认为是最新的 aws 版本 1.9.6 【参考方案1】:

如果您使用AWS SDK for Java 2.x,则可以在构建器模式中添加内容类型。

例如,将 Base64 编码的图像作为 JPEG 对象上传到 S3(假设您已经实例化了 S3 客户端):

byte[] stringAsByteArray = java.util.Base64.getDecoder().decode(base64EncodedString);

s3Client.putObject(
        PutObjectRequest.builder().bucket("my-bucket").key("my-key").contentType("image/jpg").build(),
        RequestBody.fromBytes(stringAsByteArray)
);

【讨论】:

【参考方案2】:

我可以通过命令行轻松解决这个问题,我在通过aws commandline 上传 html 文件时遇到了类似的问题,即使文件名具有正确的扩展名。

如前面的 cmets 所述,添加 --content-type 参数可以解决此问题。 执行以下命令并刷新页面返回八位字节流。

aws s3api put-object --bucket [BUCKETNAME] --body index.html  --key index.html     --profile [PROFILE] --acl public-read 

修复:添加--content type text/html

aws s3api put-object --bucket [BUCKETNAME] --body index.html  --key index.html  --profile [PROFILE] --acl public-read --content-type text/html

【讨论】:

【参考方案3】:

因为你必须在最后设置内容类型在发送之前,使用 putObject 方法;

        ObjectMetadata md = new ObjectMetadata();

        InputStream myInputStream = new ByteArrayInputStream(bFile); 
        md.setContentLength(bFile.length);
        md.setContentType("text/html");
        md.setContentEncoding("UTF-8");

        s3client.putObject(new PutObjectRequest(bucketName, keyName, myInputStream, md));

上传后,内容类型设置为“text/html

这是一个有效的虚拟代码,检查一下,我刚刚尝试过,它正在工作;

public class TestAWS 

    //TEST
    private static String bucketName = "whateverBucket";

    public static void main(String[] args) throws Exception 
        BasicAWSCredentials awsCreds = new BasicAWSCredentials("whatever", "whatever");

        AmazonS3 s3client = new AmazonS3Client(awsCreds);
        try
        
            String uploadFileName = "D:\\try.txt";
            String keyName = "newFile.txt";

            System.out.println("Uploading a new object to S3 from a file\n");
            File file = new File(uploadFileName);

            //bFile will be the placeholder of file bytes
            byte[] bFile = new byte[(int) file.length()];
            FileInputStream fileInputStream=null;

            //convert file into array of bytes  
            fileInputStream = new FileInputStream(file);
            fileInputStream.read(bFile);
            fileInputStream.close();

            ObjectMetadata md = new ObjectMetadata();

            InputStream myInputStream = new ByteArrayInputStream(bFile); 
            md.setContentLength(bFile.length);
            md.setContentType("text/html");
            md.setContentEncoding("UTF-8");

            s3client.putObject(new PutObjectRequest(bucketName, keyName, myInputStream, md));
         catch (AmazonServiceException ase)
        
            System.out.println("Caught an AmazonServiceException, which "
                    + "means your request made it "
                    + "to Amazon S3, but was rejected with an error response"
                    + " for some reason.");
            System.out.println("Error Message:    " + ase.getMessage());
            System.out.println("HTTP Status Code: " + ase.getStatusCode());
            System.out.println("AWS Error Code:   " + ase.getErrorCode());
            System.out.println("Error Type:       " + ase.getErrorType());
            System.out.println("Request ID:       " + ase.getRequestId());
         catch (AmazonClientException ace)
        
            System.out.println("Caught an AmazonClientException, which "
                    + "means the client encountered "
                    + "an internal error while trying to "
                    + "communicate with S3, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message: " + ace.getMessage());
        

    


希望对你有帮助。

【讨论】:

【参考方案4】:

您对 S3 帐户的默认 mime 内容是否有任何覆盖?查看此链接以了解如何检查它:How to override default Content Types。

无论如何,您的 S3 客户端似乎无法通过文件的内容确定正确的 mime 类型,因此它依赖于扩展名。当浏览器/servlet 无法确定 mime 类型时,八位字节流是广泛使用的默认内容 mime 类型:Is there any default mime type?

【讨论】:

【参考方案5】:

It seems那个

上传文件时,AWS S3 Java 客户端将尝试确定 如果尚未设置正确的内容类型。用户是 负责确保在上传时设置合适的内容类型 流。如果未提供内容类型且无法确定 文件名,默认内容类型,“application/octet-stream”, 将被使用。

为文件提供 .html 扩展名提供了一种设置正确类型的方法。

根据我一直在查看的示例,您显示的代码应该执行您想要执行的操作。 :/

【讨论】:

这并不能解释为什么当明确设置为“text/html”时它会重置为“application/octet-stream”。 因为内容类型似乎没有正确设置,aws 找到最合适的。但是,如果您在使用 putObject 方法之前设置了内容类型,则会设置内容类型。你可以看看我的回答。【参考方案6】:

您必须在代码中执行其他操作。我刚刚使用 1.9.6 S3 SDK 尝试了您的代码示例,文件获取了“text/html”内容类型。

这是确切的 (Groovy) 代码:

class S3Test 
    static void main(String[] args) 

        def s3 = new AmazonS3Client()

        def random = new Random()
        def bucketName = "raniz-playground"
        def keyName = "content-type-test"

        byte[] contentAsBytes = new byte[1024]
        random.nextBytes(contentAsBytes)

        ByteArrayInputStream contentsAsStream = new ByteArrayInputStream(contentAsBytes);
        ObjectMetadata md = new ObjectMetadata();
        md.setContentLength(contentAsBytes.length);
        md.setContentType("text/html");
        s3.putObject(new PutObjectRequest(bucketName, keyName, contentsAsStream, md))

        def object = s3.getObject(bucketName, keyName)
        println(object.objectMetadata.contentType)
        object.close()
    

程序打印

文本/html

S3 元数据也是如此:

以下是通过网络发送的通信(由 Apache HTTP Commons 调试日志记录提供):

>> PUT /content-type-test HTTP/1.1
>> Host: raniz-playground.s3.amazonaws.com
>> Authorization: AWS <nope>
>> User-Agent: aws-sdk-java/1.9.6 Linux/3.2.0-84-generic Java_HotSpot(TM)_64-Bit_Server_VM/25.45-b02/1.8.0_45
>> Date: Fri, 12 Jun 2015 02:11:16 GMT
>> Content-Type: text/html
>> Content-Length: 1024
>> Connection: Keep-Alive
>> Expect: 100-continue
<< HTTP/1.1 200 OK
<< x-amz-id-2: mOsmhYGkW+SxipF6S2+CnmiqOhwJ62WfWUkmZk4zU3rzkWCEH9P/bT1hUz27apmO
<< x-amz-request-id: 8706AE3BE8597644
<< Date: Fri, 12 Jun 2015 02:11:23 GMT
<< ETag: "6c53debeb28f1d12f7ad388b27c9036d"
<< Content-Length: 0
<< Server: AmazonS3

>> GET /content-type-test HTTP/1.1
>> Host: raniz-playground.s3.amazonaws.com
>> Authorization: AWS <nope>
>> User-Agent: aws-sdk-java/1.9.6 Linux/3.2.0-84-generic Java_HotSpot(TM)_64-Bit_Server_VM/25.45-b02/1.8.0_45
>> Date: Fri, 12 Jun 2015 02:11:23 GMT
>> Content-Type: application/x-www-form-urlencoded; charset=utf-8
>> Connection: Keep-Alive
<< HTTP/1.1 200 OK
<< x-amz-id-2: 9U1CQ8yIYBKYyadKi4syaAsr+7BV76Q+5UAGj2w1zDiPC2qZN0NzUCQNv6pWGu7n
<< x-amz-request-id: 6777433366DB6436
<< Date: Fri, 12 Jun 2015 02:11:24 GMT
<< Last-Modified: Fri, 12 Jun 2015 02:11:23 GMT
<< ETag: "6c53debeb28f1d12f7ad388b27c9036d"
<< Accept-Ranges: bytes
<< Content-Type: text/html
<< Content-Length: 1024
<< Server: AmazonS3

这也是查看source code 向我们展示的行为 - 如果您设置内容类型,SDK 将不会覆盖它。

【讨论】:

这并不能解释为什么当明确设置为“text/html”时它会重置为“application/octet-stream”。 :P 不,但它证明它不 =)

以上是关于为啥上传到 S3 的文件的内容类型为 application/octet-stream,除非我将文件命名为 .html?的主要内容,如果未能解决你的问题,请参考以下文章

Amazon S3 直接上传无法识别文件的内容类型

使用 AWS SDK for PHP 上传的 Amazon S3 文件总是“application/octet-stream”?

用于将 svg 图像上传到 AWS S3 的内容类型

在 Django 中,仅生成 s3 客户端 generate_presigned_post,内容类型为 mp4 文件上传

将 PDF 内容上传到 S3 存储桶

上传文件时获取不正确的文件扩展名和内容类型