AmazonS3 इनपुटस्ट्रीम लंबाई उदाहरण के साथ putObject


83

मैं जावा का उपयोग करके S3 में एक फ़ाइल अपलोड कर रहा हूं - यह वही है जो मुझे अब तक मिला है:

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()));

फ़ाइल अपलोड की जा रही है, लेकिन जब सामग्री की लंबाई निर्धारित नहीं की जा रही हो, तो एक चेतावनी दी जाती है:

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.

यह एक फ़ाइल है जिसे मैं अपलोड कर रहा हूं और streamचर एक है InputStream, जिससे मैं बाइट सरणी को इस तरह प्राप्त कर सकता हूं IOUtils.toByteArray(stream):।

इसलिए जब मैं इस तरह से सामग्री की लंबाई और MD5 ( यहाँ से लिया गया ) सेट करने का प्रयास करता हूँ :

// 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);

यह S3 से वापस आने के लिए निम्न त्रुटि का कारण बनता है:

आपके द्वारा निर्दिष्ट सामग्री-एमडी 5 अमान्य था।

मैं क्या गलत कर रहा हूं?

किसी भी मदद की सराहना की!

PS मैं Google App Engine पर हूं - मैं फ़ाइल को डिस्क पर लिखने या एक अस्थायी फ़ाइल बनाने के लिए नहीं लिख सकता क्योंकि AppEngine FileOutputStream का समर्थन नहीं करता है।


IOUtils.toByteArray पूरी फ़ाइल को आपकी मेमोरी में पढ़ें ताकि आपकी फ़ाइलों के आकार के आधार पर, यह पर्याप्त समाधान नहीं हो सके। एक बेहतर समाधान यह होगा कि आप फ़ाइल प्रदाता से फाइल साइज़ के बारे में अनुरोध करें और फिर इसे S3 में स्ट्रीमिंग कर दें, इस तरह से आपको सभी फाइलों को मैमोरी में डाउनलोड नहीं करना पड़ेगा क्योंकि आपको साइज़ के बारे में पहले से ही जानकारी है
Hamdi

जवाबों:


69

क्योंकि मूल प्रश्न का उत्तर कभी नहीं दिया गया था, और मुझे इसी समस्या में भागना पड़ा, एमडी 5 समस्या का समाधान यह है कि एस 3 नहीं चाहता कि हेक्स एन्कोडेड एमडी 5 स्ट्रिंग के बारे में हम आम तौर पर सोचते हैं।

इसके बजाय, मुझे यह करना पड़ा।

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

अनिवार्य रूप से वे एमडी 5 मूल्य के लिए क्या चाहते हैं, बेस 64 एनकोडेड रॉ एमडी 5 बाइट-सरणी है, न कि हेक्स स्ट्रिंग। जब मैंने इस पर स्विच किया तो यह मेरे लिए बहुत अच्छा काम करने लगा।


और हम एक winnahhhh है! एमडी 5 मुद्दे का जवाब देने के अतिरिक्त प्रयास के लिए धन्यवाद। यही वह हिस्सा है जिसके लिए मैं खुदाई कर रहा था ...
गीक स्टॉक्स

इस मामले में क्या सामग्री है? मुझे नहीं मिला। मैं वही चेतावनी दे रहा हूं। थोड़ी मदद, कृपया?
शॉनलाइन

@ शॉनलाइन कंटेंट इनपुटस्ट्रीम है
सरवॉन

किसी भी तरह से हेक्स से एमडी 5 बाइट-सरणी में परिवर्तित करने के लिए? यही हम अपने DB में स्टोर करते हैं।
जोएल

कृपया ध्यान दें कि meta.setContentLength (IOUtils.toByteArray (स्ट्रीम) .length); InputStream की खपत करता है। जब AWS एपीआई इसे पढ़ने की कोशिश करता है, तो यह शून्य लंबाई है और इसलिए विफल रहता है। आपको ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream (बाइट्स) से एक नई इनपुट स्ट्रीम बनाने की आवश्यकता है;
बर्नी लेनज़

43

अगर आप सभी अमेजन से कंटेंट लेंथ एरर को हल करना चाह रहे हैं तो आप इनपुट स्ट्रीम से बाइट्स को लॉन्ग तक पढ़ सकते हैं और मेटाडेटा में जोड़ सकते हैं।

/*
 * 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();
    }
}

आपको इस सटीक विधि का उपयोग करके दो बार इनपुट स्ट्रीम पढ़ने की आवश्यकता होगी ताकि यदि आप एक बहुत बड़ी फ़ाइल अपलोड कर रहे हैं तो आपको इसे एक बार एक सरणी में पढ़ना और फिर वहां से पढ़ना पड़ सकता है।


24
तो आपका निर्णय दो बार स्ट्रीम पढ़ने का है! और आप पूरी फाइल को मेमोरी में सेव करते हैं। यह S3 चेतावनी के रूप में OOM का कारण हो सकता है!
पावेल व्यंकंकिन

3
एक इनपुट स्ट्रीम का उपयोग करने में सक्षम होने की बात यह है कि आप डेटा को एक साथ सभी मेमोरी में लोड नहीं कर सकते हैं।
जॉर्डन डेविडसन

AmazonServiceException के लिए, बहुत अधिक प्रिंट करने की आवश्यकता नहीं है। getMessage विधि getErrorType को छोड़कर सब कुछ प्रिंट करती है।
14 दिसंबर को saurabheights

33

अपलोड करने के लिए, S3 SDK में दो पुटोजेक्ट तरीके हैं:

PutObjectRequest(String bucketName, String key, File file)

तथा

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

Inputstream + ObjectMetadata विधि को आपके इनपुटस्ट्रीम की सामग्री लंबाई की न्यूनतम मेटाडेटा की आवश्यकता होती है। यदि आप नहीं करते हैं, तो यह उस जानकारी को प्राप्त करने के लिए इन-मेमोरी को बफर कर देगा, यह ओओएम का कारण बन सकता है। वैकल्पिक रूप से, आप लंबाई प्राप्त करने के लिए अपनी स्वयं की मेमोरी बफ़रिंग कर सकते हैं, लेकिन फिर आपको दूसरी इनपुटस्ट्रीम प्राप्त करने की आवश्यकता है।

ओपी (उनके पर्यावरण की सीमाएं) से नहीं पूछा गया, लेकिन मेरे जैसे किसी और के लिए। मुझे यह आसान लगता है, और सुरक्षित (यदि आपके पास अस्थायी फ़ाइल तक पहुंच है), एक अस्थायी फ़ाइल में इनपुटस्ट्रीम लिखने के लिए, और अस्थायी फ़ाइल डालें। इन-मेमोरी बफर नहीं है, और दूसरा इनपुटस्ट्रीम बनाने की कोई आवश्यकता नहीं है।

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();
    }
}

CopyInputStreamToFile (inputStream, scratchFile) में दूसरा तर्क टाइप फ़ाइल या आउटपुटस्ट्रीम है?
शॉनलाइन

1
हालांकि यह आईओ गहन है, लेकिन मैं अभी भी इसके लिए वोट करता हूं। चूंकि यह बड़ी फ़ाइल ऑब्जेक्ट पर OOM से बचने का सबसे अच्छा तरीका हो सकता है। हालाँकि, कोई भी कुछ n * बाइट्स पढ़ सकता है और पार्ट फाइल बना सकता है और अलग से s3 पर अपलोड कर सकता है।
लाइनर्र

7

S3 पर लिखते समय, आपको यह सुनिश्चित करने के लिए S3 ऑब्जेक्ट की लंबाई निर्दिष्ट करने की आवश्यकता है कि स्मृति त्रुटियों से बाहर नहीं हैं।

उपयोग IOUtils.toByteArray(stream)करने से OOM त्रुटियों का भी खतरा है क्योंकि यह ByteArrayOutputStream द्वारा समर्थित है

इसलिए, सबसे अच्छा विकल्प यह है कि आप पहले स्थानीय डिस्क पर एक अस्थायी फ़ाइल में इनपुटस्ट्रीम लिखें और फिर उस फ़ाइल का उपयोग करके S3 को अस्थायी फ़ाइल की लंबाई निर्दिष्ट करके लिखें।


1
धन्यवाद, लेकिन मैं Google ऐप इंजन (अपडेटेड प्रश्न) पर हूं - डिस्क पर फ़ाइल नहीं लिख सकता, अगर मैं ऐसा कर सकता था कि मैं एक फ़ाइल लेने वाले पुटोजेब ओवरलोड का उपयोग कर सकता था :(
JohnIdol

@srikanta बस आपकी सलाह ली। अस्थायी फ़ाइल की लंबाई निर्दिष्ट करने की आवश्यकता नहीं है। बस के रूप में अस्थायी फ़ाइल पास है।
सिया सोसिबो

FYI करें अस्थायी फ़ाइल दृष्टिकोण एक विकल्प नहीं है यदि, मेरी तरह, आप सर्वर-साइड एन्क्रिप्शन को निर्दिष्ट करना चाहते हैं, जो कि ObjectMetadata में किया जाता है। दुर्भाग्य से वहाँ कोई PutObjectRequest (स्ट्रिंग बकेटनाम, स्ट्रिंग कुंजी, फ़ाइल फ़ाइल, ObjectMetadata मेटाडेटा)
केविन पाउली

@kevin पाउली आप कर सकते हैंrequest.setMetadata();
dbaq

6

मैं वास्तव में कुछ ही काम कर रहा हूँ, लेकिन मेरे AWS S3 स्टोरेज पर: -

सर्वलेट के लिए कोड जो अपलोड की गई फ़ाइल प्राप्त कर रहा है: -

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());
        }
    }
}

कोड जो इस डेटा को 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;
    }
}

नोट: - मैं क्रेडेंशियल के लिए aws properties फाइल का उपयोग कर रहा हूं।

उम्मीद है की यह मदद करेगा।


3

मैंने एक लाइब्रेरी बनाई है जो मेमोरी में सब कुछ बफर करने से बचने के लिए पृष्ठभूमि में मल्टीपार्ट अपलोड का उपयोग करता है और डिस्क पर भी नहीं लिखता है: https://github.com/alexmojaki/s3-stream-upload


-1

बस फ़ाइल ऑब्जेक्ट को पुटबॉजेक्ट विधि में पास करना मेरे लिए काम करता है। यदि आप एक स्ट्रीम प्राप्त कर रहे हैं, तो इसे S3 पर पास करने से पहले एक अस्थायी फ़ाइल में लिखने का प्रयास करें।

amazonS3.putObject(bucketName, id,fileObject);

मैं Aws SDK v1.11.414 का उपयोग कर रहा हूं

Https://stackoverflow.com/a/35904801/2373449 पर जवाब ने मेरी मदद की


यदि आपके पास एक स्ट्रीम है, तो आप उस स्ट्रीम का उपयोग करना चाहते हैं। अपने डेटा को प्राप्त करने के लिए बस (अस्थायी) फ़ाइल में लेखन अक्षम है और आपको अतिरिक्त सिरदर्द (फ़ाइल को हटाने, डिस्क का उपयोग) देता है
विचलनकर्ता

यह आपको मेटाडेटा, जैसे एन्क्रिप्शन, जो कि AWS में संग्रहीत करते समय सामान्य अभ्यास है, को पारित करने की अनुमति नहीं देगा
user1412523

-15

log4j-1.2.12.jar फ़ाइल जोड़ने से मेरे लिए समस्या हल हो गई है


2
-1: मुझे लगता है कि यह लॉग चेतावनी को छिपा देगा लेकिन त्रुटि को हल नहीं करेगा। इतना कठोर होने के लिए क्षमा करें, यह आपका पहला उत्तर है, लेकिन यह इस प्रश्न को हल नहीं करता है।
रोमलद्र
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.