Попытка записи байтов изображения из соединения с сокетом, java.lang.IllegalArgumentException: image == null - PullRequest
0 голосов
/ 07 февраля 2019

Я пытаюсь разработать собственный конвейер изображений с низкими издержками для обработки изображений с использованием прямых соединений сокетов.

Я не могу воссоздать изображения на стороне сервера, однако их невозможно открыть.

У меня есть клиент, который делает снимки экрана с моего экрана и отправляет эти снимки экрана через соединение через сокет.

Отправляемый формат данных: ds = размер данных, указывающий размер заголовка JSON, затем 8 символов (16 байтов для фактического размера заголовка в байтах), затем данные JSON, которые действуют как данные заголовка с файлом/ application application, и сразу после этого - байты изображения.

(Некоторые фоновые изображения, читать не нужно:) Я не включил байты изображения в фактическое тело JSON, потому чтоэто требовало преобразования байтов изображения в base64, что было очень неэффективно и приводило к тому, что сообщения через 25 секунд передавались на сервер.

Например:

ds222     {

"abcd_id":"1234567890",

"image_file_format":"png",

"image_encoding":"RGB_565",

"width_in_pixels":1024,

"height_in_pixels":2048,

"timestamp":"2019-01-31T20:48:59+00:00",

"data_array_size_in_bytes":"208670"
}

Вот программа тестирования клиента, которая делает снимок и передает данные:

package SocketImageClient;


public class ImageClient {



    private static final SimpleDateFormat yyyyMMddHHmmssSSSdataFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");

    private static final String[] HEADER_FOR_INCOMING_DATA_INFO = {"d", "s", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "};
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        //String ipAddress = Inet4Address.getLocalHost().getHostAddress();
        String ipAddress = "127.0.0.1";
        int port = 10002;

        int counter = 0;
          Robot r = null;
          int width=0; 
          int height=0; 
          String mime= "jpg";
          byte[] imageInByte = null; 

                while (counter < 5)
                {

            try {
                r = new Robot();
            } catch (AWTException ex) {
                Logger.getLogger(ImageClient.class.getName()).log(Level.SEVERE, null, ex);
            }


            // Used to get ScreenSize and capture image 
            Rectangle capture =  new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); 
            BufferedImage Image = r.createScreenCapture(capture); 
            try { 
                width = Image.getWidth();
                height = Image.getHeight(); 
                //get the bytes of the buffered image 
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ImageIO.write( Image, "jpg", baos );
    baos.flush();
    imageInByte = baos.toByteArray();
    baos.close();

            } catch (IOException ex) {
                Logger.getLogger(ImageClient.class.getName()).log(Level.SEVERE, null, ex);
            }

        sendImageDataTcp(ipAddress,port,"jpg",mime, width,height, yyyyMMddHHmmssSSSdataFormat.format(System.currentTimeMillis()) ,imageInByte);
        counter++;
            try {
                Thread.sleep(150);
            } catch (InterruptedException ex) {
                Logger.getLogger(ImageClient.class.getName()).log(Level.SEVERE, null, ex);
            }
            counter++;
                }

    }


    private static void sendImageDataTcp(String ipAddress, int port, String fileFormat, String MIME, int widthPixels, int heightPixels, String timeStamp, byte[] imageBytes) {

        //String ipAddress = Inet4Address.getLocalHost().getHostAddress();
        Socket socket = null;
        OutputStream os = null;

        try {


            socket = new Socket(ipAddress, port);

            if (socket != null) {
                os = socket.getOutputStream();
                //make sure byte-enconding is UTF8 for actual JSON structure
                //OutputStreamWriter out = new OutputStreamWriter(os, StandardCharsets.UTF_8);

                //test JSON
                String headerJson = "{\n"
                        + "  \"abcd_id\":\"1234567890\",\n"
                        + "  \"file_format\":\"jpg\",\n"
                        + "  \"image_compression\":\"RGB_565\",\n"
                        + "  \"width_in_pixels\":1024,\n"
                        + "  \"height_in_pixels\":2048,\n"
                        + "  \"timestamp\":\"2019-01-31T20:48:59+00:00\",\n"
                        + "  \"data_array_size_in_bytes\":\""+imageBytes.length+"\"\n"
                        + "}";

                //empty header
                String[] header_for_incoming_data_info = HEADER_FOR_INCOMING_DATA_INFO;
                //note we wan to length of  the bytes not JSONObject  length.
                String headerStringPlusSize = "ds" + headerJson.toString().getBytes().length;
                //set header based on the string  headerStringPlusSize
                for (int i = 0; i < headerStringPlusSize.length(); i++) {
                    header_for_incoming_data_info[i] = String.valueOf(headerStringPlusSize.charAt(i));
                }
                //entire entireMessage = header + json data
                StringBuilder entireMessage = new StringBuilder();
                for (int c = 0; c < header_for_incoming_data_info.length; c++) {
                    entireMessage.append(header_for_incoming_data_info[c]);
                }
                entireMessage.append(headerJson.toString());

                //write JSON HEADER
                os.write(entireMessage.toString().getBytes());
                //write image bytes  
                os.write(imageBytes);




            } else {
                //Log.e(TAG, "The the image transfer server cannot be reached!");
                System.out.println("The the image transfer server cannot be reached!");
            }

        } catch (java.net.ConnectException exc) {
            //Log.e(TAG, "The the image transfer server cannot be reached!");
            System.out.println("The the image transfer server cannot be reached!");
            exc.printStackTrace();

        } catch (IOException e) {
            //Log.e(TAG, "ex:" + e.getMessage());
            System.out.println("ex:" + e.getMessage());
            e.printStackTrace();
        } /*catch (JSONException e) {
            e.printStackTrace();
        }*/ finally {
            if (os != null) {
                try {
                    os.flush();
                    os.close();
                } catch (IOException ex) {
                    //Log.e(TAG, "ex:" + ex.getMessage());
                    System.out.println("ex:" + ex.getMessage());
                    ex.printStackTrace();
                }
            }
        }

    }//end client


}

Вот сервер сокетов, который получает байты и пытается воссоздать образ:

package socketserver;



public class SocketServer {

    /**
     * How to run the program via if you have .class & via command line java -cp
     * ./classes socketserver.SocketServer 10001
     *
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        ServerSocket serverSocket = null;

        System.out.println("1)Run the program from command prompt OR shell like by issuing this command.java -jar SocketServerGradle.jar <PORT_NUMBER>");
        System.out.println("Note: <PORT_NUMBER> must replace by actual port number. Like java -jar SocketServerGradle.jar 10001");
        System.out.println("\n");
        System.out.println("2)Any other message format, will result in errors.");

        if (args[0] != null && args[0].length() > 0) {
            //a port number must be numeric 
            int portNumber = Integer.valueOf(args[0]);

            try {
                serverSocket = new ServerSocket(portNumber);
                System.out.println("\n");
                System.out.println("***abcd to adrian image server started****");

                new Thread(new ServiceHandler(serverSocket)).start();
            } catch (Exception ex) {
                Logger.getLogger(SocketServer.class.getName()).log(Level.SEVERE, null, ex);
            }

        }//end args input validation 
        else {
        System.out.println("1)Run the program from command prompt OR shell like by issuing this command.java -jar SocketServerGradle.jar <PORT_NUMBER>");
        System.out.println("Note: <PORT_NUMBER> must replace by actual port number. Like java -jar SocketServerGradle.jar 10001");
        System.out.println("\n");
        System.out.println("2)Any other message format, will result in errors.");

        }

    }//end main 

    private static class ServiceHandler implements Runnable {

        private ServerSocket serverSocket;

        public ServiceHandler(ServerSocket _serverSocket) {
            serverSocket = _serverSocket;
        }

        public void run() {
            Socket socket = null;
            BufferedReader bf = null;
            //stream suitable for reading raw bytes
            InputStream inputStream; 
            //stream suitable for reading characters
            InputStreamReader inputStreamReader; 
            while (true) {

                try {
                    socket = serverSocket.accept();
                } catch (java.net.BindException exc) {
                    Logger.getLogger(SocketServer.class.getName()).log(Level.SEVERE, null, exc);
                    if (exc.getMessage().contains("Address") && exc.getMessage().contains("already") && exc.getMessage().contains("use")) {
                        System.out.println("Port already being used, make sure to kill previous instance of program.");
                        errorLog(exc.getMessage());
                    }
                } catch (IOException ex) {
                    errorLog(ex.getMessage());
                    ex.printStackTrace();
                }

                try {

                    if (socket != null) {
                        inputStream = socket.getInputStream(); 
                        inputStreamReader = new InputStreamReader(inputStream);

                        int read = 0;
                        int counter = 0;
                        long size = 0;
                        //read the 1st 10 character (20 bytes) to get the header 
                        StringBuilder header = new StringBuilder();
                        while ((read = inputStreamReader.read()) != -1 && counter < 11) {
                            header.append((char) read);
                            counter++;
                        }
                        //ok inputStream the header valid, if the 1st two characters are ds then yes. 
                        if (header.length() > 1 && header.substring(0, 2).equalsIgnoreCase("ds")) {
                            System.out.println("Valid Header");

                            //now lets get the message size, which comes after the 2nd char until the 12th char. RThe size will be parsed into Long numeric value. 
                            try {
                                String sizedata = header.substring(2, 11).trim();
                                size = Long.valueOf(sizedata);
                                System.out.println("HEADER size in bytes:" + size);
                            } catch (NumberFormatException exc) {
                                exc.printStackTrace();
                                errorLog(exc.getMessage());
                            }
                            //now that we have  the message size lets get the entire header which inputStream in JSON 
                            StringBuffer message = new StringBuffer();

                            long byteCounter = 0;
                            while ((read = inputStreamReader.read()) != -1 && byteCounter < size) {
                                message.append((char) read);
                                byteCounter++;
                            }

                            ImagePayload imagePayload = parseJson(message.toString());

                            if (imagePayload != null) {
                                String logMsg = "parsed JSON & created:" + imagePayload;
                                System.out.println(logMsg);
                                logInfo(logMsg);

                                //after the JSON , get teh actual size of the image bytes that are sent after  the JSON header 
                                int actualImageBytesSize = imagePayload.getDataArraySizeInBytes();
                                System.out.println("Image size in bytes:" + actualImageBytesSize);
                                //get the image bytes
                                int imageBytesCounter = 0;

                                ByteArrayOutputStream byteos = new ByteArrayOutputStream();
                                byte [] buffer = new byte[1024];
                                int currentBytesRead=0; 
                                int totalBytesRead=0; 
                                while ((currentBytesRead = inputStream.read(buffer)) != -1) {
                                    byteos.write(buffer,0,currentBytesRead);
                                    totalBytesRead= totalBytesRead+ currentBytesRead; 
                                }
                                //log info 
                                String info = "total bytes Read:"+totalBytesRead+"expected number of bytes:"+actualImageBytesSize; 
                                System.out.println(info);
                                logInfo(info); 
                                //get the byte array and write the image to disk 
                                byteos.flush();
                                byte[] imageData = byteos.toByteArray();

                                BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(imageData));

                                try
                                {
                                 //write
                                File imageFile = new File("." + File.separator + imagePayload.getabcdId()+"."+imagePayload.getImageFileFormat());
                //ERROR IS HERE -> attempt #1 
                               if (!imageFile.exists()) {
                                        imageFile.createNewFile();
                                    }
                                ImageIO.write(bufferedImage,imagePayload.getImageFileFormat(), imageFile );

                                 //ERROR IS HERE attempt # 2 - although the file is written its it cant be opened. Windows creates JPG file but cant open it.  
                                /*FileOutputStream fos = new FileOutputStream(imageFile);
                                fos.write(imageData);
                                fos.close();*/

                                }
                               catch (java.lang.IllegalArgumentException exc)
                                {
                                     exc.printStackTrace();
                                }
                                catch (IOException exc)
                                {
                                    exc.printStackTrace();
                                }


                            } else {
                                String logErr = "Unable! to parse JSON::" + message.toString();
                                System.out.println(logErr);
                                logInfo(logErr);
                            }

                        } else {
                            System.err.println("invalid header!");
                            logInfo("invalid header!");
                        }

                    }

                } catch (StringIndexOutOfBoundsException exc) {
                    exc.printStackTrace();
                    errorLog(exc.getMessage());
                } catch (IOException ex) {
                    errorLog(ex.getMessage());
                    ex.printStackTrace();
                }

            }//end while 
        }
    }

    private static ImagePayload parseJson(String message) {
        ImagePayload imagePayload = null;
        try {
            //parse JSON

            ObjectMapper mapper = new ObjectMapper();
            JsonFactory factory = mapper.getFactory();
            com.fasterxml.jackson.core.JsonParser parser = factory.createParser(message.toString());
            //Jackson JSON Tree Model
            JsonNode jacksonNode = mapper.readTree(parser);
            if (jacksonNode != null) {
                JsonNode abcdIdNode = jacksonNode.get("abcd_id");
                JsonNode imageFileFormatNode = jacksonNode.get("file_format");
                JsonNode imageEncodingNode = jacksonNode.get("image_compression");
                JsonNode imageWidthInPixelsNode = jacksonNode.get("width_in_pixels");
                JsonNode imageHeightInPixelsNode = jacksonNode.get("height_in_pixels");
                JsonNode dataArraySizeInBytesNode = jacksonNode.get("data_array_size_in_bytes");
                JsonNode timestampNode = jacksonNode.get("timestamp");

                String abcdId = abcdIdNode.textValue();
                String imageFileFormat = imageFileFormatNode.textValue();
                String imageCompression= imageEncodingNode.textValue();
                int width = imageWidthInPixelsNode.intValue();
                int height = imageHeightInPixelsNode.intValue();
                int dataArraySizeInBytes = dataArraySizeInBytesNode.asInt();
                String timestamp = timestampNode.asText();

                imagePayload = new ImagePayload(abcdId, imageFileFormat, imageCompression, width, height, dataArraySizeInBytes, timestamp);

            } else {
                String errorMsg = "Error parsing json, JsonNode=" + jacksonNode + ", message:" + message;
                System.err.println(errorMsg);
                errorLog(errorMsg);
            }
        } catch (IOException ex) {
            String err  = "ImagePayload parseJson(), message:"+message+",exc:"+ex.getMessage();
            System.out.println(err);
            ex.printStackTrace();
            errorLog(err);
        }
        catch (NullPointerException exc)
        {
            String err  = "ImagePayload parseJson(), message:"+message+",exc:"+exc.getMessage();
            System.out.println(err);
            exc.printStackTrace();
            errorLog(err);
        }

        return imagePayload;
    }

    private static void logInfo(String message) {
        try {
            File logFile = new File("." + File.separator + "abcd_adrian_IMG_INFO_LOG.log");
            if (!logFile.exists()) {
                logFile.createNewFile();
            }
            message = "\n" + message;
            Files.write(Paths.get(logFile.getAbsolutePath()), message.getBytes(), StandardOpenOption.APPEND);
        } catch (IOException ex) {

            ex.printStackTrace();

        }

    }

    private static void errorLog(String message) {
        try {
            File logFile = new File("." + File.separator + "abcd_adrian_IMG_ERROR_LOG.log");
            if (!logFile.exists()) {
                logFile.createNewFile();
            }
            message = "\n" + message;
            Files.write(Paths.get(logFile.getAbsolutePath()), message.getBytes(), StandardOpenOption.APPEND);
        } catch (IOException ex) {

            ex.printStackTrace();

        }

    }
}

Попытка # 1 Вывод CMD (соответствующие фрагменты):

*** abcd к серверу изображений adrian запущен **** Действительный заголовок

Размер заголовка в байтах: 219

parsed JSON & создал: ImagePayload {abcdId = 1234567890, ImageFileFormat = jpg, ImageEncoding = RGB_565, WidthInPixels = 1024, HeightInPixels = 2048, DataArraySizeInBytes = 132561, метка времени = 2019-01-31T20: 48: 59 + 00: 00: 00

Размер изображения в байтах: 132561 Всего байт. Чтение: 124369 Ожидаемое количество байтов: 132561

java.lang.IllegalArgumentException : image == null!в javax.imageio.ImageTypeSpecifier.createFromRenderedImage (ImageTypeSpecifier.java:925) в javax.imageio.ImageIO.getWriter (ImageIO.java:1592) в javax.imageio.ImageIO.write (ImageIO.java:verScket $ socket)ServiceHandler.run (SocketServer.java:216) на java.lang.Thread.run (Thread.java:748)

Я поиграл с жестким кодированием формата в JPEG, JPEG, PNG, PNG и т. Д.Однако я получил тот же IllegalArgumentException, который был брошен.

Я также заметил, как вы можете видеть в журналах, что сокет, возможно, не прочитал целые байты?

всего байтов Прочитано: 124369 Ожидаемое количествобайт: 132561

Попытка # 2 Вывод CMD (соответствующие фрагменты):

*** abcd к серверу изображений adrian запущен ****

Действительный размер заголовка заголовка в байтах: 219 разобрано JSON и создано: ImagePayload {abcdId = 1234567890, ImageFileFormat = jpg, ImageEncoding = RGB_565, WidthInPixels = 1024, HeightInPixels = 2048, DataArraySizeInBytes = 129209: 201122, время: 201122: время данных:48: 59 + 00: 00} Размер изображения в байтах: 129122

всего байт. Чтение: 121161 ожидаемое количество байтов: 129122

Для попытки № 2 Вместо использования:

 ImageIO.write(bufferedImage,imagePayload.getImageFileFormat(), imageFile );

Я использовал:

FileOutputStream fos = new FileOutputStream(imageFile);
                                fos.write(imageData);
                                fos.close();

В этот момент я смог записать файл на диск, , однако я не смог открытьit. Когда я нажимаю на файл Windows OS, появляется сообщение «похоже, мы не поддерживаем этот формат файла».Хотя файл распознается как JPG в файловой системе.

Есть какие-нибудь мысли о том, почему не читаются все байты ИЛИ как я могу исправить этот код, чтобы отправлять и мой заголовок JSON, и байты изображения за один снимок?

UPADTE,ОСНОВНЫЕ КОММЕНТАРИИ:

Я включил обновленный сниппет для клиента из-за ограничения Stackoverflow, в основном клиента пишется JSON + image [] байтов

    new DataOutputStream(os).writeUTF(headerJson);
    //write image bytes  
    os.write(imageBytes);

Соответствующий фрагмент сервера:

  if (socket != null) {
                        inputStream = socket.getInputStream(); 
                        DataInputStream dis = new DataInputStream(inputStream);
                        String message = dis.readUTF();
                         //now that we have  the message size lets get the entire header which inputStream in JSON 


                            ImagePayload imagePayload = parseJson(message);

                            if (imagePayload != null) {
                                String logMsg = "parsed JSON & created:" + imagePayload;
                                System.out.println(logMsg);
                                logInfo(logMsg);

                                //after the JSON , get teh actual size of the image bytes that are sent after  the JSON header 
                                int actualImageBytesSize = imagePayload.getDataArraySizeInBytes();
                                //get the image bytes
                                int imageBytesCounter = 0;

                                ByteArrayOutputStream byteos = new ByteArrayOutputStream();
                                byte [] buffer = new byte[1024];
                                int currentBytesRead=0; 
                                int totalBytesRead=0; 
                                while ((currentBytesRead = inputStream.read(buffer)) != -1) {
                                    byteos.write(buffer,0,currentBytesRead);
                                    totalBytesRead= totalBytesRead+ currentBytesRead; 
                                }
                                //log info 
                                String info = "total bytes Read:"+totalBytesRead+",expected number of bytes:"+actualImageBytesSize; 
                                System.out.println(info);
                                logInfo(info); 
                                //get the byte array and write the image to disk 
                                byteos.flush();
                                byte[] imageData = byteos.toByteArray();

                                BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(imageData));

                                try
                                {
                                 //write
                                File imageFile = new File("." + File.separator + imagePayload.getWamsId()+"."+imagePayload.getImageFileFormat());
                               if (!imageFile.exists()) {
                                        imageFile.createNewFile();
                                    }
                                //Attempt #1 
                                ImageIO.write(bufferedImage,"jpeg", imageFile );

                                 //attempt # 2 - although the file is written its it cant be opened 
                                /*FileOutputStream fos = new FileOutputStream(imageFile);
                                fos.write(imageData);
                                fos.close();*/

                                }
                               catch (java.lang.IllegalArgumentException exc)
                                {
                                     exc.printStackTrace();
                                }
                                catch (IOException exc)
                                {
                                    exc.printStackTrace();
                                }


                            } else {
                                String logErr = "Unable! to parse JSON::" + message.toString();
                                System.out.println(logErr);
                                logInfo(logErr);
                            }

                    }

Программа теперь работает отлично, но только на Windows.Когда я перемещаю код сервера в linux Amazon AWS, сервер работает, получает JSON правильно и создает образ, но изображение не может быть снова открыто ??Почему изображение (JPG) может быть открыто только тогда, когда сервер работает под Windows?

Я получаю это исключение только на Linux:

total bytes Read:15805440,expected number of bytes:15805440
java.lang.IllegalArgumentException: image == null!
        at javax.imageio.ImageTypeSpecifier.createFromRenderedImage(Unknown Source)
        at javax.imageio.ImageIO.getWriter(Unknown Source)
        at javax.imageio.ImageIO.write(Unknown Source)
        at socketserver.SocketServer$ServiceHandler.run(SocketServer.java:186)
        at java.lang.Thread.run(Unknown Source)
total bytes Read:15805440,expected number of bytes:15805440
java.lang.IllegalArgumentException: image == null!
        at javax.imageio.ImageTypeSpecifier.createFromRenderedImage(Unknown Source)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...