2009-03-26 7 views
35

मैं आईबीएम वेबस्पेयर एप्लिकेशन सर्वर v6 और जावा 1.4 का उपयोग कर रहा हूं और उपयोगकर्ता को डाउनलोड करने के लिए ServletOutputStream पर बड़ी सीएसवी फाइलें लिखने की कोशिश कर रहा हूं। इस समय फाइलें 50-750 एमबी से हैं।सर्विसलेट के बिना जावा सर्वलेट में बहुत बड़ी फ़ाइलों को लिखने के लिए ServletOutputStream का उपयोग

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

इन फ़ाइलों को केवल HTTPS पर प्रमाणीकृत उपयोगकर्ताओं को ही सेवा दी जा सकती है, यही कारण है कि मैं उन्हें अपाचे में चिपकाने के बजाय Servlet के माध्यम से उनकी सेवा कर रहा हूं।

कोड मैं का उपयोग कर रहा है (कुछ फुलाना इस चारों ओर निकाला गया) है:

resp.setHeader("Content-length", "" + fileLength); 
    resp.setContentType("application/vnd.ms-excel"); 
    resp.setHeader("Content-Disposition","attachment; filename=\"export.csv\""); 

    FileInputStream inputStream = null; 

    try 
    { 
     inputStream = new FileInputStream(path); 
     byte[] buffer = new byte[1024]; 
     int bytesRead = 0; 

     do 
     { 
      bytesRead = inputStream.read(buffer, offset, buffer.length); 
      resp.getOutputStream().write(buffer, 0, bytesRead); 
     } 
     while (bytesRead == buffer.length); 

     resp.getOutputStream().flush(); 
    } 
    finally 
    { 
     if(inputStream != null) 
      inputStream.close(); 
    } 

FileInputStream के रूप में अगर मैं एक फ़ाइल पर लिखने या बस पूरी तरह से लिखने को दूर एक समस्या उत्पन्न कर नहीं लगता है स्मृति उपयोग एक समस्या प्रतीत नहीं होता है।

मैं क्या सोच रहा हूं कि resp.getOutputStream().write को स्मृति में संग्रहीत किया जा रहा है जब तक कि डेटा क्लाइंट के माध्यम से नहीं भेजा जा सके। इसलिए पूरी फ़ाइल को resp.getOutputStream() में पढ़ा और संग्रहीत किया जा सकता है जिससे मेरी मेमोरी समस्याएं और क्रैश हो रही है!

मैं इन धाराओं बफ़रिंग की कोशिश की है और यह भी java.nio, मेरी स्मृति मुद्दों के अंतर के किसी भी बिट कर रहा है जिनमें से कोई भी से चैनल उपयोग करने की कोशिश। लूप के प्रति पुनरावृत्ति के बाद और लूप के बाद, मैंने मदद नहीं की, OutputStream भी फ्लश किया है।

+2

कोशिश इस Websphere वेब कंटेनर कस्टम गुण की स्थापना - com.ibm.ws.webcontainer.channelwritetype = सिंक विवरण यहां हैं - http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/ index.jsp? topic =/com.ibm.websphere.express.doc/info/exp/ae/rweb_custom_props.html –

उत्तर

39

औसत सभ्य सर्वलेटकेंटर स्वयं प्रत्येक ~ 2 केबी डिफ़ॉल्ट रूप से स्ट्रीम को फ्लश करता है। अनुक्रमिक रूप से एक और एक ही स्रोत से डेटा स्ट्रीम करते समय आपको HttpServletResponse के OutputStream पर flush() पर स्पष्ट रूप से flush() पर कॉल करने की आवश्यकता नहीं होनी चाहिए। उदाहरण के लिए टोमकैट (और वेबस्पेयर!) यह HTTP कनेक्टर की bufferSize विशेषता के रूप में कॉन्फ़िगर करने योग्य है।

औसत सभ्य सर्वलेट कंटेनर भी chunks में डेटा को स्ट्रीम करता है यदि सामग्री की लंबाई पहले से अज्ञात है (Servlet API specification के अनुसार!) और यदि क्लाइंट HTTP 1.1 का समर्थन करता है।

समस्या लक्षण कम से कम संकेत देते हैं कि servletcontainer फ़्लशिंग से पहले स्मृति में पूरी स्ट्रीम को बफर कर रहा है। इसका अर्थ यह हो सकता है कि सामग्री लंबाई शीर्षलेख सेट नहीं है और/या servletcontainer chunked एन्कोडिंग का समर्थन नहीं करता है और/या क्लाइंट पक्ष खंडित एन्कोडिंग का समर्थन नहीं करता है (यानी यह HTTP 1.0 का उपयोग कर रहा है)।

एक या अन्य, सामग्री पहले से लंबाई सिर्फ सेट ठीक करने के लिए:

response.setHeader("Content-Length", String.valueOf(new File(path).length())); 
1

flush आउटपुट स्ट्रीम पर काम करता है।

वास्तव में मैं टिप्पणी करना चाहता था कि आपको लिखने के तीन-तर्क रूप का उपयोग करना चाहिए क्योंकि बफर आवश्यक रूप से पूरी तरह से पढ़ा नहीं जाता है (विशेष रूप से फ़ाइल (!) के अंत में)। इसके अलावा एक प्रयास/आखिरकार क्रम में होगा जबतक कि आप चाहते हैं कि आप अप्रत्याशित रूप से सर्वर मर जाए।

+0

फ्लश आउटपुटस्ट्रीम पर काम करता है। हाँ, इसमें इसके चारों ओर एक कोशिश/आखिरकार ब्लॉक है, इनपुटस्ट्रीम इसमें बंद है। मैंने पढ़ने और लिखने दोनों के 1 और 3-तर्क संस्करण दोनों के साथ प्रयास किया है और पठनीयता के लिए ऐसा कोई फर्क नहीं पड़ता है क्योंकि मैंने पोस्ट में 1 तर्क संस्करण का उपयोग किया था। – Martin

0

अपनी स्मृति समस्याओं से संबंधित नहीं है, जबकि पाश होना चाहिए:

while(bytesRead > 0); 
+0

हम्म अगर मैं उस समय लूप सेट करता हूं तो यह कभी भी आउटपुट स्ट्रीम में कुछ भी नहीं लिखता है। जब तक मैं लूप के बाहर प्रारंभिक पढ़ नहीं लेता।शायद मुझे इसके बजाए इसका उपयोग करना चाहिए ((बाइट्स रीड = इनपुटस्ट्रीम.read (बफर, ऑफसेट, बफर। लम्बाई))! = -1) सुरक्षित होगा। किसी भी तरह से असंबंधित :( – Martin

+0

चेतावनी: 0 बाइट्स लौटाना पूरी तरह से संभव है और लूप को समाप्त नहीं करना चाहिए। – eckes

1

मैं एक वर्ग कि outputstream वह अन्य संदर्भों में पुन: प्रयोज्य बनाने के लिए लपेटता इस्तेमाल किया है। यह ब्राउज़र के लिए तेजी से डेटा प्राप्त करने में मेरे लिए अच्छा काम करता है, लेकिन मैंने स्मृति प्रभावों को नहीं देखा है। (मेरे पुराने M_ चर नामकरण क्षमा कृपया)

import java.io.IOException; 
import java.io.OutputStream; 

public class AutoFlushOutputStream extends OutputStream { 

    protected long m_count = 0; 
    protected long m_limit = 4096; 
    protected OutputStream m_out; 

    public AutoFlushOutputStream(OutputStream out) { 
     m_out = out; 
    } 

    public AutoFlushOutputStream(OutputStream out, long limit) { 
     m_out = out; 
     m_limit = limit; 
    } 

    public void write(int b) throws IOException { 

     if (m_out != null) { 
      m_out.write(b); 
      m_count++; 
      if (m_limit > 0 && m_count >= m_limit) { 
       m_out.flush(); 
       m_count = 0; 
      } 
     } 
    } 
} 
1

मैं भी यकीन नहीं है अगर इस मामले में flush()ServletOutputStream पर काम करता है, लेकिन ServletResponse.flushBuffer() ग्राहक के जवाब (कम से कम प्रति 2.3 सर्वलेट कल्पना) भेजना चाहिए।

ServletResponse.setBufferSize() भी वादा करता है।

1

तो, अपने परिदृश्य के बाद, क्या आप इसके अंदर फ्लश (आईएनजी) नहीं होना चाहिए, जबकि लूप (प्रत्येक पुनरावृत्ति पर), इसके बाहर के बजाय? हालांकि, मैं थोड़ा बड़ा बफर के साथ कोशिश करता हूं।

1
  1. केविन के वर्ग यदि यह पास() ऑपरेटर में अशक्त नहीं है m_out क्षेत्र बंद हो जाना चाहिए, हम नहीं चीजों को रिसाव करना चाहते हैं, क्या हम?

  2. साथ ही ServletOutputStream.flush() ऑपरेटर, HttpServletResponse.flushBuffer() ऑपरेशन भी बफर को फ्लश कर सकता है। हालांकि, यह एक कार्यान्वयन विशिष्ट विवरण प्रतीत होता है कि इन परिचालनों का कोई प्रभाव पड़ता है या नहीं, या http सामग्री लंबाई समर्थन हस्तक्षेप कर रहा है या नहीं। याद रखें, सामग्री-लंबाई निर्दिष्ट करना HTTP 1.0 पर एक विकल्प है, इसलिए यदि आप चीजों को फ्लश करते हैं तो चीजों को केवल स्ट्रीम करना चाहिए। लेकिन मैं नहीं दिख रहा है

+0

1) यह बहस योग्य है। कक्षा ने स्ट्रीम नहीं बनाया, इसलिए आप तर्क दे सकते हैं कि इसका कोई स्वामित्व नहीं है और उसे करीबी परिचालन नहीं करना चाहिए। – Renan

1

कि जब हालत काम नहीं करता है, तो आप -1 जाँच करने के लिए उपयोग करने से पहले की जरूरत है। और कृपया आउटपुट स्ट्रीम के लिए एक अस्थायी चर का उपयोग करें, इसके नाइसर को पढ़ने के लिए और यह getOutputStream() को दोबारा कॉल करने से सुरक्षित करता है।

OutputStream outStream = resp.getOutputStream(); 
while(true) { 
    int bytesRead = inputStream.read(buffer); 
    if (bytesRead < 0) 
     break; 
    outStream.write(buffer, 0, bytesRead); 
} 
inputStream.close(); 
out.close(); 
0

आपके कोड में एक अनंत लूप है।

do 
{ 
    bytesRead = inputStream.read(buffer, offset, buffer.length); 
    resp.getOutputStream().write(buffer, 0, bytesRead); 
} 
while (bytesRead == buffer.length); 

ऑफसेट पाश thoughout मान समान है, इसलिए यदि शुरू में ऑफसेट = 0, यह जो होगा सुराग OOM त्रुटि के लिए हर चरण में ऐसा ही रहेगा और जो अनंत लूप का कारण होगा।

-1

आईबीएम websphere एप्लिकेशन सर्वर डिफ़ॉल्ट रूप से servlets के लिए एसिंक्रोनस डेटा स्थानांतरण का उपयोग करता है। इसका मतलब है कि यह प्रतिक्रिया बफर करता है। यदि आपको बड़े डेटा और आउटऑफमेमरी अपवादों में समस्याएं हैं, तो सिंक्रोनस मोड का उपयोग करने के लिए WAS पर सेटिंग्स बदलने का प्रयास करें।

Setting the WebSphere Application Server WebContainer to synchronous mode

तुम भी लोड हो रहा है मात्रा का ख्याल रखना और उन्हें फ्लश चाहिए। बड़ी फ़ाइल से लोड करने के लिए नमूना।

ServletOutputStream os = response.getOutputStream(); 
FileInputStream fis = new FileInputStream(file); 
      try { 
       int buffSize = 1024; 
       byte[] buffer = new byte[buffSize]; 
       int len; 
       while ((len = fis.read(buffer)) != -1) { 
        os.write(buffer, 0, len); 
        os.flush(); 
        response.flushBuffer(); 
       } 
      } finally { 
       os.close(); 
      } 
संबंधित मुद्दे