2015-03-31 101 views
1

我試圖用Java創建一個程序,它會截取用戶的屏幕截圖,壓縮圖像並通過套接字將其發送到服務器。 由於某種原因,圖像最終保存損壞(無法讀取)。你能幫我找出問題所在嗎?通過套接字發送壓縮的JPG圖像

CLIENT:(截圖輸入爲一個BufferedImage,然後返回的字節數組被返回到它發送到服務器的第二功能)

public static byte[] compressImage(BufferedImage image) throws IOException { 
    System.out.println("starting compression"); 

    ByteArrayOutputStream os = new ByteArrayOutputStream(37628); 

    float quality = 0.16f; 

    // create a BufferedImage as the result of decoding the supplied InputStream 

    // get all image writers for JPG format 
    Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg"); 
    //Iterator iter = ImageIO.getImageWritersByFormatName("jpeg"); 

    if (!writers.hasNext()) 
     throw new IllegalStateException("No writers found"); 

    ImageWriter writer = (ImageWriter) writers.next(); 
    ImageOutputStream ios = ImageIO.createImageOutputStream(os); 
    writer.setOutput(ios); 

    ImageWriteParam param = writer.getDefaultWriteParam(); 

    // compress to a given quality 
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 
    param.setCompressionQuality(quality); 

    // appends a complete image stream containing a single image and 
    //associated stream and image metadata and thumbnails to the output 
    writer.write(null, new IIOImage(image, null, null), param); 
    os.flush(); 

    return os.toByteArray(); 
} 

    public void uploadShot(byte[] imgData, String nickname) { 

    try { 
     /* Try to connect to the server on localhost, port 5555 */ 

     Socket sk = new Socket("localhost", 23232); 
     OutputStream output = sk.getOutputStream(); 

     /* Send filename to server */ 

     OutputStreamWriter outputStream = new OutputStreamWriter(sk.getOutputStream()); 
     outputStream.write(nickname + "\n"); 
     outputStream.flush(); 

     /* Get response from server */ 

     BufferedReader inReader = new BufferedReader(new InputStreamReader(sk.getInputStream())); 

     String serverStatus = inReader.readLine(); // Read the first line 

     /* If server is ready, send the file */ 

     if (serverStatus.equals("READY")){ 
      int len = imgData.length; 
      int start = 0; 

      if (len < 0) 
       throw new IllegalArgumentException("Negative length not allowed"); 
      if (start < 0 || start >= imgData.length) 
       throw new IndexOutOfBoundsException("Out of bounds: " + start); 
      // Other checks if needed. 

      // May be better to save the streams in the support class; 
      // just like the socket variable. 
      OutputStream out = sk.getOutputStream(); 
      DataOutputStream dos = new DataOutputStream(out); 

      dos.writeInt(len); 
      if (len > 0) { 
       dos.write(imgData, start, len); 
      } 
      dos.close(); 
      output.close(); 
      sk.close(); 

      System.out.println("Transfer complete."); 
     } 
    } catch (Exception ex){ 
     /* Catch any errors */ 
     System.out.println(ex.getMessage()); 
    } 
} 

SERVER:(所接收到的圖像被保存到)

public static void main(String args[]) throws Exception{ 
    System.out.println("Server running..."); 

    /* Listen on port 5555 */ 

    ServerSocket server = new ServerSocket(23232); 

    /* Accept the sk */ 

    Socket sk = server.accept(); 

    System.out.println("Server accepted client"); 
    InputStream input = sk.getInputStream(); 
    BufferedReader inReader = new BufferedReader(new InputStreamReader(sk.getInputStream())); 
    BufferedWriter outReader = new BufferedWriter(new OutputStreamWriter(sk.getOutputStream())); 

    /* Read the filename */ 
    String nickname = inReader.readLine(); 

    if (!nickname.equals("")){ 

     /* Reply back to client with READY status */ 

     outReader.write("READY\n"); 
     outReader.flush(); 
    } 


    String current = "/home/kasgel/screenshots"; 

    DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy__HH:mm:ss"); 
    Date timestamp = new Date(); 
    File filename = new File(current + "/" + nickname + "-" + dateFormat.format(timestamp) + ".jpg"); 
    if (!filename.exists()) { 
     filename.createNewFile(); 
    } 
    FileOutputStream wr = new FileOutputStream(filename); 

    byte[] buffer = new byte[sk.getReceiveBufferSize()]; 

    int bytesReceived = 0; 

    while((bytesReceived = input.read(buffer))>0) { 
     wr.write(buffer,0,bytesReceived); 
    } 
    wr.close(); 
} 

與時間戳提到的文件夾和錯誤消息打開保存的屏幕截圖時,我得到如下: display.im6:不是一個JPEG文件:使用0x00 0x03時`MyNick-30-03啓動-2015__19:27:58.jpg'@ error/jpeg .C/JPEGErrorHandler/316。

+1

發送一些已知數據並進行比較。 – sstn 2015-03-31 13:26:50

+2

你附加一個'BufferedReader'並同時使用底層的'InputStream',所以我會說前者「吃掉」了你的一些數據,所以從後者讀取的內容將缺少圖片的頭部。 – dnet 2015-03-31 13:33:38

回答

1

當你寫你的形象,你先寫含在圖像的字節長度的32位有符號整數:

 dos.writeInt(len); 
     if (len > 0) { 
      dos.write(imgData, start, len); 
     } 

但是,當你正在閱讀的圖像回來,你不」先讀長度;您正在讀取所有數據(包括長度),就好像它們是圖像的一部分一樣。

雖然你有第二個問題,那本身也會導致這個問題。當您創建BufferedReader並調用readLine時,它將讀取超出換行符 - 它將讀取,直到其緩衝區已滿。如果你繼續讀取它,這不是一個問題,但是在讀完這行後,你繼續從底層的InputStream中讀取數據,這會在換行之後消耗更多的字節。

解決方法是:只使用一個抽象來讀/寫數據。在這種情況下,最簡單的方法是使用DataOutputStreamDataInputStream。使用writeUTF寫入文件名並使用readUTF讀回。用writeInt寫下文件的長度,然後用readInt讀回。用write寫入數據並用read讀取數據 - 並確保只讀取您從readInt調用收到的字節數。最重要的是,繼續使用相同的DataOutputStreamDataInputStream實例;請勿在相同底層構造緩衝讀取器和輸入流InputStream