Отправка сжатого изображения JPG через сокет

1

Я пытаюсь создать программу с Java, которая снимает скриншот экрана пользователя, сжимает изображение и отправляет его на сервер через сокеты. По какой-то причине изображение сохраняется в конце (оно не читается). Можете ли вы помочь мне найти проблему?

КЛИЕНТ: (скриншот вводится как 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
    Отправьте некоторые известные данные и сравните.
  • 2
    Вы присоединяете BufferedReader и одновременно используете базовый InputStream , поэтому я бы сказал, что первый «съедает» некоторые из ваших данных, поэтому содержимое, считываемое из последнего, будет лишено заголовка изображения.
Теги:
sockets

1 ответ

1
Лучший ответ

Когда вы пишете свое изображение, сначала пишете 32-разрядное целое число со знаком, содержащее длину в байтах изображения:

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

Но когда вы читаете изображение назад, вы сначала не читаете длину; вы читаете все данные (включая длину), как если бы они были частью изображения.

Однако у вас есть вторая проблема, которая сама по себе также вызовет эту проблему. Когда вы создаете BufferedReader и вызываете readLine на нем, он будет читать дальше новой строки - он будет читать, пока его буфер не будет заполнен. Это не проблема, если вы продолжаете читать с нее, но после прочтения строки вы продолжаете читать из базового InputStream, у которого часто будет больше байтов, потребляемых им после новой строки.

Решение: используйте только одну абстракцию для чтения/записи данных. В этом случае проще всего использовать DataOutputStream и DataInputStream. Напишите имя файла, используя writeUTF и прочитайте его с помощью readUTF. Напишите длину файла с помощью writeInt и прочитайте его с помощью readInt. Напишите данные с write и прочитайте ее с read - и убедитесь, что вы читаете только столько байтов, сколько вы получили от вызова readInt. И самое главное, продолжайте использовать те же DataOutputStream и DataInputStream; не создавать буферизованные считыватели и входные потоки в одном и том же базовом InputStream

Ещё вопросы

Сообщество Overcoder
Наверх
Меню