AmazonS3 putObject avec InputStream longueur exemple

je télécharge un fichier vers S3 en utilisant Java - c'est ce que j'ai obtenu jusqu'à présent:

AmazonS3 s3 = new AmazonS3Client(new BasicAWSCredentials("XX","YY"));

List<Bucket> buckets = s3.listBuckets();

s3.putObject(new PutObjectRequest(buckets.get(0).getName(), fileName, stream, new ObjectMetadata()));

le fichier est en cours de téléchargement mais un avertissement est émis lorsque je ne règle pas la longueur du contenu:

com.amazonaws.services.s3.AmazonS3Client putObject: No content length specified for stream > data.  Stream contents will be buffered in memory and could result in out of memory errors.

c'est un fichier que je télécharge et la variable stream est un InputStream , à partir duquel je peux obtenir le tableau des octets comme ceci: IOUtils.toByteArray(stream) .

donc quand j'essaie de définir le contenu longueur et MD5 (tiré de ici ) comme ceci:

// get MD5 base64 hash
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(IOUtils.toByteArray(stream));
byte[] resultByte = messageDigest.digest();
String hashtext = new String(Hex.encodeHex(resultByte));

ObjectMetadata meta = new ObjectMetadata();
meta.setContentLength(IOUtils.toByteArray(stream).length);
meta.setContentMD5(hashtext);

Il provoque l'erreur suivante à revenir de S3:

le contenu-MD5 que vous avez spécifié n'était pas valide.

Qu'est-ce que je fais de mal?

toute aide appréciée!

P.S. je suis sur Google App Engine - Je ne peux pas écrire le fichier sur le disque ou crée un fichier temporaire car AppEngine ne supporte pas FileOutputStream.

68
demandé sur Community 2011-12-02 08:45:59

7 réponses

parce que la question originale n'a jamais été répondue, et j'ai dû courir dans ce même problème, la solution pour le problème MD5 est que S3 ne veut pas la chaîne codée HEX MD5 à laquelle nous pensons normalement.

à la place, j'ai dû faire ça.

// content is a passed in InputStream
byte[] resultByte = DigestUtils.md5(content);
String streamMD5 = new String(Base64.encodeBase64(resultByte));
metaData.setContentMD5(streamMD5);

essentiellement ce qu'ils veulent pour la valeur MD5 est le tableau MD5 brut codé Base64 byte-array, pas la chaîne Hex. Quand je suis passé à ça, ça a commencé à bien marcher pour moi.

58
répondu MarcG 2017-10-26 20:31:41

si tout ce que vous essayez de faire est de résoudre l'erreur de longueur de contenu d'amazon, alors vous pouvez juste lire les octets du flux d'entrée à un Long et ajouter cela aux métadonnées.

/*
 * Obtain the Content length of the Input stream for S3 header
 */
try {
    InputStream is = event.getFile().getInputstream();
    contentBytes = IOUtils.toByteArray(is);
} catch (IOException e) {
    System.err.printf("Failed while reading bytes from %s", e.getMessage());
} 

Long contentLength = Long.valueOf(contentBytes.length);

ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(contentLength);

/*
 * Reobtain the tmp uploaded file as input stream
 */
InputStream inputStream = event.getFile().getInputstream();

/*
 * Put the object in S3
 */
try {

    s3client.putObject(new PutObjectRequest(bucketName, keyName, inputStream, metadata));

} catch (AmazonServiceException ase) {
    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("Error Message: " + ace.getMessage());
} finally {
    if (inputStream != null) {
        inputStream.close();
    }
}

vous aurez besoin de lire le flux d'entrée deux fois en utilisant cette méthode exacte donc si vous téléchargez un très grand fichier, vous pourriez avoir besoin de regarder à la lecture une fois dans un tableau et puis la lecture à partir de là.

37
répondu tarka 2016-09-19 07:06:47

pour le téléchargement, le SDK S3 a deux méthodes putObject:

PutObjectRequest(String bucketName, String key, File file)

et

PutObjectRequest(String bucketName, String key, InputStream input, ObjectMetadata metadata)

la méthode inputstream+ObjectMetadata nécessite un minimum de métadonnées de la longueur du contenu de votre entrée. Si vous ne le faites pas, alors il va tampon en mémoire pour obtenir cette information, ce qui pourrait causer OOM. Alternativement, vous pouvez faire votre propre mémoire tampon pour obtenir la longueur, mais alors vous devez obtenir un second flux entrant.

Pas demandé par les OP (limites de son environnement), mais pour quelqu'un d'autre, comme moi. Je trouve plus facile, et plus sûr (si vous avez accès au fichier temp), d'écrire le flux entrant dans un fichier temp, et mettre le fichier temp. Pas de mémoire tampon, ni d'exigence de créer un second flux entrant.

AmazonS3 s3Service = new AmazonS3Client(awsCredentials);
File scratchFile = File.createTempFile("prefix", "suffix");
try {
    FileUtils.copyInputStreamToFile(inputStream, scratchFile);    
    PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, id, scratchFile);
    PutObjectResult putObjectResult = s3Service.putObject(putObjectRequest);

} finally {
    if(scratchFile.exists()) {
        scratchFile.delete();
    }
}
23
répondu Peter Dietz 2016-09-19 07:07:08

en écrivant à S3, vous devez spécifier la longueur de l'objet S3 pour être sûr qu'il n'y a pas d'erreurs de mémoire.

utilisant IOUtils.toByteArray(stream) est également sujet à des erreurs OOM parce que cela est soutenu par ByteArrayOutputStream

Donc, la meilleure option est d'abord écrire le inputstream à un fichier temporaire sur le disque local, puis utiliser ce fichier pour écrire S3 en spécifiant la longueur de fichier temporaire.

6
répondu srikfreak 2011-12-02 05:28:17

je fais en fait un peu la même chose mais sur mon stockage AWS S3: -

Code pour servlet qui reçoit le fichier téléchargé: -

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import com.src.code.s3.S3FileUploader;

public class FileUploadHandler extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();

        try{
            List<FileItem> multipartfiledata = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);

            //upload to S3
            S3FileUploader s3 = new S3FileUploader();
            String result = s3.fileUploader(multipartfiledata);

            out.print(result);
        } catch(Exception e){
            System.out.println(e.getMessage());
        }
    }
}

Code qui télécharge ces données en tant qu'objet AWS: -

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

import org.apache.commons.fileupload.FileItem;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;

public class S3FileUploader {


    private static String bucketName     = "***NAME OF YOUR BUCKET***";
    private static String keyName        = "Object-"+UUID.randomUUID();

    public String fileUploader(List<FileItem> fileData) throws IOException {
        AmazonS3 s3 = new AmazonS3Client(new ClasspathPropertiesFileCredentialsProvider());
        String result = "Upload unsuccessfull because ";
        try {

            S3Object s3Object = new S3Object();

            ObjectMetadata omd = new ObjectMetadata();
            omd.setContentType(fileData.get(0).getContentType());
            omd.setContentLength(fileData.get(0).getSize());
            omd.setHeader("filename", fileData.get(0).getName());

            ByteArrayInputStream bis = new ByteArrayInputStream(fileData.get(0).get());

            s3Object.setObjectContent(bis);
            s3.putObject(new PutObjectRequest(bucketName, keyName, bis, omd));
            s3Object.close();

            result = "Uploaded Successfully.";
        } 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());

           result = result + ase.getMessage();
        } 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.");

           result = result + ace.getMessage();
         }catch (Exception e) {
             result = result + e.getMessage();
       }

        return result;
    }
}

Note :- j'utilise le fichier de propriétés aws pour les justificatifs d'identité.

Espérons que cette aide.

4
répondu streak 2014-03-21 10:19:22

j'ai créé une bibliothèque qui utilise des uploads multiparts en arrière-plan pour éviter de tout tamponner en mémoire et qui n'écrit pas non plus sur le disque: https://github.com/alexmojaki/s3-stream-upload

3
répondu Alex Hall 2015-10-22 14:11:43

ajouter log4j-1.2.12.fichier jar a résolu le problème pour moi

-10
répondu Rajesh 2016-12-27 20:57:22