Я пытаюсь разработать собственный конвейер изображений с низкими издержками для обработки изображений с использованием прямых соединений сокетов.
Я не могу воссоздать изображения на стороне сервера, однако их невозможно открыть.
У меня есть клиент, который делает снимки экрана с моего экрана и отправляет эти снимки экрана через соединение через сокет.
Отправляемый формат данных: 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)