通过 AWS SDK 从 AWS S3 存储桶下载文件会损坏文件

时间:2021-07-21 14:30:35

标签: amazon-web-services amazon-s3

我正在使用 java 代码和 aws sdk 从我的 aws s3 中的存储桶下载文件 (zip)。 但是,下载的文件已损坏。 手动下载文件有效。

我比较了文件的内容并注意到损坏的文件包含某种未编码的字符 看到这个: enter image description here

用于下载的代码如下:

public boolean downloadFile(String bucketName,  String fileNameOnServer, String localFileName )
    {
        S3Object object =null;
        InputStream objectData =null;
        try
        {
            object = s3.getObject(new GetObjectRequest(bucketName, fileNameOnServer));
            objectData = object.getObjectContent();
        }
        catch(Exception e)
        {
            LoggingService.writeToLog("Error001 downloading file "+bucketName+"/"+fileNameOnServer+" to "+localFileName, e, LogModule.CommonUtils,LogLevel.Error);
            return false;
        }
        try
        {
            FileWriter fw = new FileWriter(localFileName, true);
            BufferedWriter bw = new BufferedWriter(fw);
            try
            {
                BufferedReader reader = new BufferedReader(new InputStreamReader(object.getObjectContent()));
                String line;                                                
                while( (line = reader.readLine() ) !=null)
                {
                    bw.write(line);
                    bw.newLine();
                }
                LoggingService.writeToLog("file from "+bucketName+"/"+fileNameOnServer+" "+" downloaded to "+bucketName + " successfully",  LogModule.CommonUtils,LogLevel.Info);
                return true;
            }
            catch(IOException e)
            {
                LoggingService.writeToLog("Error downloading file "+bucketName+"/"+fileNameOnServer+" to "+localFileName, e, LogModule.CommonUtils,LogLevel.Error);
                return false;
            }
            finally
            {
                objectData.close();
                bw.close();
                object.close();
            }

        }
        catch(IOException e)
        {
            LoggingService.writeToLog("Error opening local file "+localFileName+" for writing ", e, LogModule.CommonUtils,LogLevel.Error);
            return false;
        }
    }

2 个答案:

答案 0 :(得分:2)

InputStreamReader 的文档包括:

<块引用>

InputStreamReader 是从字节流到字符流的桥梁:它读取字节并使用指定的字符集将它们解码为字符。它使用的字符集可以通过名称指定或明确给出,或者可以接受平台的默认字符集。

换句话说,它试图将数据视为文本数据。对于 zip 文件中的二进制数据,这几乎肯定会损坏数据。相反,如果你直接读写字节,你会传递它们而不改变它们:

package com.exampleapp.app;

import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;

public class App
{
    public static void main(String[] args)
    {
        downloadFile("example-bucket", "example-key.zip", "local-file.zip");
    }

    static AmazonS3 s3 = AmazonS3ClientBuilder.standard().withRegion(Regions.DEFAULT_REGION).build();

    public static boolean downloadFile(String bucketName,  String fileNameOnServer, String localFileName )
    {
        S3Object object = null;
        InputStream objectData = null;
        InputStream reader = null;
        OutputStream writer = null;
        try
        {
            object = s3.getObject(new GetObjectRequest(bucketName, fileNameOnServer));
            objectData = object.getObjectContent();
        }
        catch(Exception e)
        {
            System.out.println("Error001 downloading file "+bucketName+"/"+fileNameOnServer+" to "+localFileName);
            return false;
        }
        try
        {
            File file = new File(localFileName);
            try
            {
                reader = new BufferedInputStream(object.getObjectContent());
                writer = new BufferedOutputStream(new FileOutputStream(file));
                int read = -1;
                while ( ( read = reader.read() ) != -1 )
                {
                    writer.write(read);
                }
                System.out.println("file from "+bucketName+"/"+fileNameOnServer+" "+" downloaded to "+bucketName + " successfully");
                return true;
            }
            catch(IOException e)
            {
                System.out.println("Error downloading file "+bucketName+"/"+fileNameOnServer+" to "+localFileName);
                return false;
            }
            finally
            {
                object.close();
                writer.flush();
                writer.close();
                reader.close();
            }

        }
        catch(IOException e)
        {
            System.out.println("Error opening local file "+localFileName+" for writing ");
            return false;
        }
    }
}

答案 1 :(得分:2)

Photo Spring BOOT 应用程序现在支持下载图像。它完美地工作。您应该使用适用于 Java V2 的 AWS 开发工具包。这是示例应用程序:

enter image description here

当我打开下载的图像时 - 它有效且未损坏:

enter image description here

从 Amazon S3 存储桶下载此图像的代码位于 Spring 控制器中:

  @RequestMapping(value = "/downloadphoto", method = RequestMethod.GET)
    void buildDynamicReportDownload(HttpServletRequest request, HttpServletResponse response) {
        try {

            //Get the photo object name
            String photoKey = request.getParameter("photoKey");
            byte[] photoBytes = s3Client.getObjectBytes("myphotobucket", photoKey) ;
            InputStream is = new ByteArrayInputStream(photoBytes);

            //define the required information to download the image
            response.setContentType("image/png");
            response.setHeader("Content-disposition", "attachment; filename="+photoKey);
            org.apache.commons.io.IOUtils.copy(is, response.getOutputStream());
            response.flushBuffer();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

V2 S3 代码在这里。注意它读取一个对象并返回一个字节[]。

public byte[] getObjectBytes (String bucketName, String keyName) {

        s3 = getClient();

        try {
            // create a GetObjectRequest instance
            GetObjectRequest objectRequest = GetObjectRequest
                    .builder()
                    .key(keyName)
                    .bucket(bucketName)
                    .build();

            // get the byte[] from this AWS S3 object
            ResponseBytes<GetObjectResponse> objectBytes = s3.getObjectAsBytes(objectRequest);
            byte[] data = objectBytes.asByteArray();
            return data;

        } catch (S3Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return null;
    }

最后是 Javascript 调用

function DownloadImage(){

    //Post the values to the controller
    var photo =  $('#photo').val();
    window.location="../downloadphoto?photoKey=" + photo ;
}

此示例将被更新,因此所有这些代码都在示例文档中。

https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/javav2/usecases/creating_photo_analyzer_app