2010-01-17 5 views
10

मैं एक http पथ के लिए स्थानीय फ़ाइल सिस्टम से एक बड़ी वीडियो/छवि फ़ाइल को प्रकाशित करने की कोशिश कर रहा हूँ, लेकिन मैं कुछ समय के बाद स्मृति त्रुटि के बाहर एक में चलाने ...OutputStream OutOfMemoryError जब HTTP

यहाँ भेज रहा है कोड

public boolean publishFile(URI publishTo, String localPath) throws Exception { 
    InputStream istream = null; 
    OutputStream ostream = null; 
    boolean isPublishSuccess = false; 

    URL url = makeURL(publishTo.getHost(), this.port, publishTo.getPath()); 
    HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 


    if (conn != null) { 

     try { 

      conn.setDoOutput(true); 
      conn.setDoInput(true); 
      conn.setRequestMethod("PUT"); 
      istream = new FileInputStream(localPath); 
      ostream = conn.getOutputStream(); 

      int n; 
      byte[] buf = new byte[4096]; 
      while ((n = istream.read(buf, 0, buf.length)) > 0) { 
       ostream.write(buf, 0, n); //<--- ERROR happens on this line.......??? 
      } 

      int rc = conn.getResponseCode(); 

      if (rc == 201) { 
       isPublishSuccess = true; 
      } 

     } catch (Exception ex) { 
      log.error(ex); 
     } finally { 
      if (ostream != null) { 
       ostream.close(); 
      } 

      if (istream != null) { 
       istream.close(); 
      } 
     } 
    } 

    return isPublishSuccess; 

} 

यहाँ त्रुटि मैं हो रही है,

Exception in thread "Thread-8773" java.lang.OutOfMemoryError: Java heap space 
    at java.util.Arrays.copyOf(Arrays.java:2786) 
    at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:94) 
    at sun.net.www.http.PosterOutputStream.write(PosterOutputStream.java:61) 
    at com.test.HTTPClient.publishFile(HTTPClient.java:110) 
    at com.test.HttpFileTransport.put(HttpFileTransport.java:97) 
+0

कुछ (मेरे साथ) इसे क्रॉसपोस्ट के लिए कठोर समझते हैं: http://forums.sun.com/thread.jspa?threadID=5424210 विशेष रूप से जब आप इस तथ्य का भी उल्लेख नहीं करते हैं। –

+0

कृपया उस संपादन पर अपराध न करें जहां मैंने आपके कोड की आलोचना की थी। यह औसत से बेहतर है, लेकिन इसमें सुधार के लिए जगह है। सभी गैर-तुच्छ कोड करता है। और अपवाद हैंडलिंग को गड़बड़ करना आसान है: मुझे एक सप्ताह या उससे पहले एक अच्छी तरह से योग्य -1 मिल गया था जब मैंने बस एक उदाहरण में टाइप किया था, तो मेरे कंपाइलर को चेक किए बिना कोशिश/पकड़/अंत में। – kdgregory

उत्तर

13

HttpUrlConnection डेटा को बफर कर रहा है ताकि यह Content-Length शीर्षलेख (प्रति HTTP spec) सेट कर सके।

एक विकल्प, यदि आपका गंतव्य सर्वर इसका समर्थन करता है, तो "chunked" स्थानान्तरण का उपयोग करना है। यह एक समय में डेटा का केवल एक छोटा सा हिस्सा बफर करेगा। हालांकि, सभी सेवाएं इसका समर्थन नहीं करतीं (अमेज़ॅन एस 3, उदाहरण के लिए, नहीं)।

एक और विकल्प (और बेहतर एक बेहतर) Jakarta HttpClient का उपयोग करना है। आप फ़ाइल से अनुरोध में "इकाई" सेट कर सकते हैं, और कनेक्शन कोड अनुरोध हेडर उचित रूप से सेट करेगा।


संपादित करें: ओपन स्कूल टिप्पणी की है कि ओपी HttpURLConnection.setFixedLengthStreamingMode(long length) कह सकते हैं। मैं इस विधि से अनजान था; इसे 1.5 में जोड़ा गया था, और तब से मैंने इस वर्ग का उपयोग नहीं किया है।

हालांकि, मैं अभी भी जकार्ता HttpClient का उपयोग करने का सुझाव देता हूं, सरल कारण यह है कि यह ओपी को बनाए रखने वाले कोड की मात्रा को कम करता है। कोड जो बॉयलरप्लेट है, अभी भी त्रुटियों की संभावना है:

  • ओपी सही ढंग से इनपुट और आउटपुट के बीच प्रतिलिपि बनाने के लिए लूप को संभालती है। आम तौर पर जब मैं इसका एक उदाहरण देखता हूं, तो पोस्टर या तो लौटे हुए बफर आकार की जांच नहीं करता है, या बफर को फिर से आवंटित करता रहता है। बधाई हो, लेकिन अब आपको यह सुनिश्चित करना होगा कि आपके उत्तराधिकारी ज्यादा ध्यान दें।
  • अपवाद हैंडलिंग काफी अच्छा नहीं है। हां, ओपी finally ब्लॉक में कनेक्शन बंद करने के लिए याद करता है, और फिर, उस पर बधाई। सिवाय इसके कि close() कॉलों में से कोई भी IOException फेंक सकता है, दूसरे को निष्पादित करने से रोक सकता है। और पूरी तरह से विधि Exception फेंकता है, ताकि संकलक समान त्रुटियों को पकड़ने में मदद नहीं करेगा।
  • मैं कोड सेट करने और निष्पादित करने के लिए कोड की 31 लाइनों की गणना करता हूं (प्रतिक्रिया कोड जांच और यूआरएल गणना को छोड़कर, लेकिन कोशिश/पकड़/आखिरकार सहित)। एचटीपी क्लाइंट के साथ, यह आधा दर्जन एलओसी की सीमा में कहीं होगा।

यहां तक ​​कि अगर ओ पी पूरी तरह से इस कोड को लिखा था, और जकार्ता कॉमन्स आईओ में उन लोगों के समान तरीकों में पुनर्संशोधित, s/वह है कि ऐसा नहीं करना चाहिए। यह कोड दूसरों द्वारा लिखित और परीक्षण किया गया है।मुझे पता है कि यह मेरा समय बर्बाद करने का समय बर्बाद है, और संदेह है कि यह ओपी के समय का कचरा भी है।

+4

इस मामले में, फ़ाइल आकार के आधार पर HttpURLConnection.setFixedLengthStreamingMode भी कहा जा सकता है। यह HttpUrlConnection को प्रत्यक्ष स्ट्रीम वापस करने का कारण बनता है क्योंकि इसे सामग्री की लंबाई को समझने के लिए बफर नहीं करना पड़ता है। – nos

+0

नंबर सही है। एक खंडित अनुरोध का उपयोग करने या इसके लिए तीसरे पक्ष के पुस्तकालयों का उपयोग करने की बिल्कुल आवश्यकता नहीं है। बस कनेक्ट करने से पहले HttpURLConnection पर फ़ाइल लंबाई के साथ setFixedLengthStreamingMode को कॉल करें और GetOutputStream सॉकेट के आउटपुटस्ट्रीम पर एक गैर-बफरिंग प्रत्यक्ष रैपर वापस कर देगा। – jarnbjo

+1

@jarnbjo - मुझे लगता है कि आप वह व्यक्ति हैं जिन्होंने मुझे और हर किसी को जवाब दिया जो जवाब दे रहा था। और जब आपकी टिप्पणियां मूल्यवान होती हैं, तो उन्हें प्रतिक्रिया के रूप में पोस्ट करने के लिए और अधिक रचनात्मक होता। – kdgregory

-1

आपकी समस्या यह है कि आप राम के एक्स/एन बाइट्स में एक्स वीडियो बाइट ठीक करने के लिए कोशिश कर रहे हैं ... जब एन> 1.

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

अपना ढेर आकार जांचें। यदि आप डिफ़ॉल्ट ले चुके हैं तो आप इसे बढ़ाने के लिए -Xmx का उपयोग कर सकते हैं।

2

समस्या यह है कि HttpURLConnection क्लास आपके डेटा को स्टोर करने के लिए बाइट सरणी का उपयोग कर रही है। संभवतः यह वीडियो जिसे आप दबा रहे हैं, उपलब्ध होने से अधिक मेमोरी ले रहा है। आपके पास यहां कुछ विकल्प हैं:

  1. अपने एप्लिकेशन में स्मृति बढ़ाएं। आप अपने आवेदन में 1 जीबी मेमोरी देने के लिए -Xmx1024m विकल्प का उपयोग कर सकते हैं। यह आपके द्वारा स्मृति में संग्रहीत डेटा की मात्रा में वृद्धि करेगा।

  2. यदि आप अभी भी स्मृति से बाहर हैं, तो आप वीडियो को धक्का देने के लिए एक और लाइब्रेरी की कोशिश करने पर विचार करना चाहेंगे जो डेटा को सभी में स्मृति में संग्रहीत नहीं करता है। अपाचे कॉमन्स एचटीपी क्लाइंट में ऐसी सुविधा है। अधिक जानकारी के लिए इस साइट को देखें: http://hc.apache.org/httpclient-3.x/features.html। बड़ी फ़ाइलों के अनेक भागों प्रपत्र अपलोड करने के लिए इस खंड देखें: http://hc.apache.org/httpclient-3.x/methods/multipartpost.html

2

बुनियादी प्राप्त आपरेशन के अलावा और कुछ के लिए, बिल्ट-इन java.net HTTP सामान बहुत अच्छा नहीं है। इसके लिए Apache Commons HttpClient का उपयोग करना अनुशंसित है। यह आपको इस तरह की अधिक सहज सामग्री प्रदान करने देता है:

PutMethod put = new PutMethod(url); 
put.setRequestEntity(new FileRequestEntity(localFile, contentType)); 
int responseCode = put.executeMethod(); 

जो आपके बहुत सारे बॉयलर-प्लेट कोड को प्रतिस्थापित करता है।

3
conn.setFixedLengthStreamingMode((int) new File(localpath).length()); 

और बफरिंग आप BufferedOutputStream और BufferedInputStream

का अच्छा उदाहरण में अपने धाराओं को कवर कर सकता के लिए chunked तुम वहाँ मिल सकता है अपलोड करने: gdata-java-client

1

HttpsURLConnection # setChunkedStreamingMode (1024 * 1024 * 10); // 10 एमबी खंड यह सुनिश्चित करता है कि आंतरिक बफरिंग के बिना, किसी भी फ़ाइल (किसी भी आकार का) https कनेक्शन पर स्ट्रीम किया जाता है। इसका उपयोग तब किया जाना चाहिए जब फ़ाइल का आकार या सामग्री की लंबाई अज्ञात हो।