Work on sending files
This commit is contained in:
@@ -1313,7 +1313,7 @@ public class ActionHandler {
|
||||
JSONObject m = messages.getJSONObject(i);
|
||||
String senderId = m.getString("sender_id");
|
||||
String senderName = m.optString("sender_name", "Other");
|
||||
String content = m.getString("content");
|
||||
String content = m.optString("content", "");
|
||||
String time = m.getString("send_at");
|
||||
|
||||
String label = senderId.equals(Session.currentUser.getString("internal_uuid")) ? "You" : senderName;
|
||||
|
||||
@@ -84,25 +84,54 @@ public class MessageDatabase {
|
||||
|
||||
public static boolean insertAttachmentsTx(Connection conn, UUID messageId, List<FileAttachment> attachments) throws SQLException {
|
||||
if (attachments == null || attachments.isEmpty()) return true;
|
||||
String sql = """
|
||||
INSERT INTO message_attachments
|
||||
(attachment_id, message_id, file_url, file_type, file_name, file_size, mime_type, width, height, duration_seconds, thumbnail_url)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
|
||||
final String sql = """
|
||||
INSERT INTO message_attachments(
|
||||
attachment_id, message_id,
|
||||
file_url, file_type, file_name, file_size, mime_type,
|
||||
width, height, duration_seconds, thumbnail_url,
|
||||
media_key, storage_path
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
""";
|
||||
|
||||
try (PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
for (FileAttachment att : attachments) {
|
||||
ps.setObject(1, UUID.randomUUID());
|
||||
ps.setObject(2, messageId);
|
||||
ps.setString(3, att.getFileUrl());
|
||||
ps.setString(4, att.getFileType()); // IMAGE/VIDEO/AUDIO/FILE/GIF/STICKER
|
||||
ps.setString(5, att.getFileName());
|
||||
if (att.getFileSize() != null) ps.setLong(6, att.getFileSize()); else ps.setNull(6, java.sql.Types.BIGINT);
|
||||
ps.setString(7, att.getMimeType());
|
||||
if (att.getWidth() != null) ps.setInt(8, att.getWidth()); else ps.setNull(8, java.sql.Types.INTEGER);
|
||||
if (att.getHeight() != null) ps.setInt(9, att.getHeight()); else ps.setNull(9, java.sql.Types.INTEGER);
|
||||
if (att.getDurationSeconds() != null) ps.setInt(10, att.getDurationSeconds()); else ps.setNull(10, java.sql.Types.INTEGER);
|
||||
ps.setString(11, att.getThumbnailUrl());
|
||||
if (att == null) throw new IllegalArgumentException("Attachment is null");
|
||||
UUID attachmentId = att.getAttachmentId() != null ? att.getAttachmentId() : UUID.randomUUID();
|
||||
UUID mediaKey = att.getMediaKey() != null ? att.getMediaKey() : attachmentId; // سادهترین حالت
|
||||
|
||||
String ft = att.getFileType();
|
||||
if (!"IMAGE".equalsIgnoreCase(ft) && !"AUDIO".equalsIgnoreCase(ft)) {
|
||||
throw new IllegalArgumentException("file_type must be IMAGE or AUDIO");
|
||||
}
|
||||
if (att.getStoragePath() == null || att.getStoragePath().isBlank()) {
|
||||
throw new IllegalArgumentException("storage_path is required for socket downloads");
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
ps.setObject(i++, attachmentId);
|
||||
ps.setObject(i++, messageId);
|
||||
//file url (display link)
|
||||
if (att.getFileUrl() == null || att.getFileUrl().isBlank()) ps.setNull(i++, java.sql.Types.VARCHAR);
|
||||
else ps.setString(i++, att.getFileUrl());
|
||||
|
||||
ps.setString(i++, ft.toUpperCase());
|
||||
ps.setString(i++, att.getFileName());
|
||||
if (att.getFileSize() == null) ps.setNull(i++, java.sql.Types.BIGINT); else ps.setLong(i++, att.getFileSize());
|
||||
if (att.getMimeType() == null) ps.setNull(i++, java.sql.Types.VARCHAR); else ps.setString(i++, att.getMimeType());
|
||||
if (att.getWidth() == null) ps.setNull(i++, java.sql.Types.INTEGER); else ps.setInt(i++, att.getWidth());
|
||||
if (att.getHeight() == null) ps.setNull(i++, java.sql.Types.INTEGER); else ps.setInt(i++, att.getHeight());
|
||||
if (att.getDurationSeconds() == null) ps.setNull(i++, java.sql.Types.INTEGER); else ps.setInt(i++, att.getDurationSeconds());
|
||||
if (att.getThumbnailUrl() == null || att.getThumbnailUrl().isBlank()) ps.setNull(i++, java.sql.Types.VARCHAR);
|
||||
else ps.setString(i++, att.getThumbnailUrl());
|
||||
|
||||
ps.setObject(i++, mediaKey);
|
||||
ps.setString(i++, att.getStoragePath());
|
||||
|
||||
ps.addBatch();
|
||||
|
||||
att.setAttachmentId(attachmentId);
|
||||
att.setMediaKey(mediaKey);
|
||||
}
|
||||
ps.executeBatch();
|
||||
return true;
|
||||
@@ -110,15 +139,24 @@ public class MessageDatabase {
|
||||
}
|
||||
|
||||
|
||||
public static boolean saveMessageWithOptionalAttachments(UUID messageId, UUID senderId, UUID receiverId,
|
||||
String receiverType, String content, String messageType,
|
||||
List<FileAttachment> attachments) {
|
||||
|
||||
public static boolean saveMessageWithOptionalAttachments(
|
||||
UUID messageId, UUID senderId, UUID receiverId,
|
||||
String receiverType, String content, String messageType,
|
||||
List<FileAttachment> attachments
|
||||
) {
|
||||
Connection conn = null;
|
||||
try {
|
||||
conn = ConnectionDb.connect();
|
||||
conn.setAutoCommit(false);
|
||||
|
||||
boolean isText = "TEXT".equalsIgnoreCase(messageType);
|
||||
boolean isText = "TEXT".equalsIgnoreCase(messageType);
|
||||
boolean isImage = "IMAGE".equalsIgnoreCase(messageType);
|
||||
boolean isAudio = "AUDIO".equalsIgnoreCase(messageType);
|
||||
if (!isText && !isImage && !isAudio) {
|
||||
throw new IllegalArgumentException("messageType must be TEXT, IMAGE, or AUDIO");
|
||||
}
|
||||
|
||||
if (isText) {
|
||||
if (attachments != null && !attachments.isEmpty())
|
||||
throw new IllegalArgumentException("TEXT must not have attachments");
|
||||
@@ -127,9 +165,18 @@ public class MessageDatabase {
|
||||
} else {
|
||||
if (attachments == null || attachments.isEmpty())
|
||||
throw new IllegalArgumentException("Non-TEXT must have at least one attachment");
|
||||
|
||||
for (FileAttachment a : attachments) {
|
||||
if (a == null) throw new IllegalArgumentException("Attachment is null");
|
||||
String ft = a.getFileType();
|
||||
if (isImage && !"IMAGE".equalsIgnoreCase(ft))
|
||||
throw new IllegalArgumentException("All attachments must be IMAGE for messageType=IMAGE");
|
||||
if (isAudio && !"AUDIO".equalsIgnoreCase(ft))
|
||||
throw new IllegalArgumentException("All attachments must be AUDIO for messageType=AUDIO");
|
||||
}
|
||||
}
|
||||
|
||||
insertMessageTx(conn, messageId, senderId, receiverId, receiverType, content, messageType);
|
||||
insertMessageTx(conn, messageId, senderId, receiverId, receiverType, content, messageType.toUpperCase());
|
||||
if (!isText) insertAttachmentsTx(conn, messageId, attachments);
|
||||
|
||||
conn.commit();
|
||||
|
||||
@@ -2,18 +2,22 @@ package org.to.telegramfinalproject.Models;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FileAttachment {
|
||||
|
||||
private final String fileUrl;
|
||||
private final String fileType; // IMAGE, VIDEO, AUDIO, FILE, GIF, STICKER
|
||||
private final String fileName;
|
||||
private final Long fileSize;
|
||||
private final String mimeType; // e.g., image/png
|
||||
private final Integer width;
|
||||
private final Integer height;
|
||||
private final Integer durationSeconds; // for audio/video
|
||||
private final String thumbnailUrl;
|
||||
private UUID attachmentId; // اختیاری؛ اگر null بود، تولید میکنیم
|
||||
private UUID mediaKey;
|
||||
private String fileUrl;
|
||||
private String fileType; // IMAGE, VIDEO, AUDIO, FILE, GIF, STICKER
|
||||
private String fileName;
|
||||
private Long fileSize;
|
||||
private String mimeType; // e.g., image/png
|
||||
private Integer width;
|
||||
private Integer height;
|
||||
private Integer durationSeconds; // for audio/video
|
||||
private String thumbnailUrl;
|
||||
private String storagePath;
|
||||
|
||||
public FileAttachment(String fileUrl,
|
||||
String fileType,
|
||||
@@ -39,6 +43,10 @@ public class FileAttachment {
|
||||
this(fileUrl, fileType, null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public FileAttachment() {
|
||||
}
|
||||
|
||||
|
||||
// ساخت از JSON /upload
|
||||
public static FileAttachment fromUploadJson(JSONObject j) {
|
||||
return new FileAttachment(
|
||||
@@ -54,7 +62,6 @@ public class FileAttachment {
|
||||
);
|
||||
}
|
||||
|
||||
// خروجی JSON برای RT/کلاینت
|
||||
public JSONObject toJson() {
|
||||
JSONObject out = new JSONObject()
|
||||
.put("file_url", fileUrl)
|
||||
@@ -123,4 +130,33 @@ public class FileAttachment {
|
||||
", thumbnailUrl='" + thumbnailUrl + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public UUID getAttachmentId() {return attachmentId;
|
||||
}
|
||||
|
||||
public UUID getMediaKey() {return mediaKey;
|
||||
}
|
||||
|
||||
public String getStoragePath() {return storagePath;
|
||||
}
|
||||
|
||||
public void setAttachmentId(UUID attachmentId) {this.attachmentId = attachmentId;
|
||||
}
|
||||
|
||||
public void setMediaKey(UUID mediaKey) {this.mediaKey = mediaKey;
|
||||
}
|
||||
|
||||
|
||||
public void setFileUrl(String fileUrl) {this.fileUrl = fileUrl;
|
||||
}
|
||||
public void setFileType(String fileType){this.fileType = fileType;}
|
||||
public void setFileName(String fileName){this.fileName = fileName;}
|
||||
public void setFileSize(Long fileSize){this.fileSize = fileSize;}
|
||||
public void setMimeType(String mimeType){this.mimeType = mimeType;}
|
||||
public void setWidth(int width){this.width = width;}
|
||||
public void setHeight(int height){this.height = height;}
|
||||
public void setDurationSeconds(Integer durationSeconds){this.durationSeconds = durationSeconds;}
|
||||
public void setThumbnailUrl(String thumbnailUrl){this.thumbnailUrl = thumbnailUrl;}
|
||||
public void setStoragePath(String storagePath) {this.storagePath = storagePath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ import org.to.telegramfinalproject.Utils.GroupPermissionUtil;
|
||||
import java.io.*;
|
||||
import java.net.Socket;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
@@ -2609,68 +2612,177 @@ public class ClientHandler implements Runnable {
|
||||
}
|
||||
|
||||
|
||||
// private void handleMediaFrame(DataInputStream dis, PrintWriter out) {
|
||||
// try {
|
||||
// // MAGIC = "MDM1"
|
||||
// final int MAGIC_EXPECTED = 0x4D444D31;
|
||||
// int magic = dis.readInt();
|
||||
// if (magic != MAGIC_EXPECTED) {
|
||||
// out.println(new JSONObject().put("status","error").put("message","bad magic").toString());
|
||||
// out.flush();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// int headerLen = dis.readInt();
|
||||
// if (headerLen <= 0 || headerLen > (64 * 1024)) {
|
||||
// out.println(new JSONObject().put("status","error").put("message","bad header length").toString());
|
||||
// out.flush();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// byte[] headerBytes = dis.readNBytes(headerLen);
|
||||
// if (headerBytes.length != headerLen) {
|
||||
// out.println(new JSONObject().put("status","error").put("message","header truncated").toString());
|
||||
// out.flush();
|
||||
// return;
|
||||
// }
|
||||
// JSONObject h = new JSONObject(new String(headerBytes, java.nio.charset.StandardCharsets.UTF_8));
|
||||
//
|
||||
// long contentLen = dis.readLong();
|
||||
// long MAX_MEDIA = 25L * 1024 * 1024;
|
||||
// if (contentLen <= 0 || contentLen > MAX_MEDIA) {
|
||||
// skip(dis, contentLen);
|
||||
// out.println(new JSONObject().put("status","error").put("message","file too large/invalid").toString());
|
||||
// out.flush();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// UUID messageId = UUID.fromString(h.getString("message_id"));
|
||||
// UUID senderId = UUID.fromString(h.getString("sender_id"));
|
||||
// String rType = h.getString("receiver_type"); // private/group/channel
|
||||
// UUID receiverId = UUID.fromString(h.getString("receiver_id"));
|
||||
// String messageType = h.getString("message_type"); // IMAGE | AUDIO
|
||||
//
|
||||
// if (!"IMAGE".equalsIgnoreCase(messageType) && !"AUDIO".equalsIgnoreCase(messageType)) {
|
||||
// skip(dis, contentLen);
|
||||
// out.println(new JSONObject().put("status","error").put("message","unsupported message_type").toString());
|
||||
// out.flush();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// String fileName = h.optString("file_name", "file.bin");
|
||||
// String mimeType = h.optString("mime_type", "application/octet-stream");
|
||||
// String text = h.optString("text", "");
|
||||
//
|
||||
// Integer width = h.has("width") && !h.isNull("width") ? h.getInt("width") : null;
|
||||
// Integer height = h.has("height") && !h.isNull("height") ? h.getInt("height") : null;
|
||||
//
|
||||
// if (fileName.length() > 200) fileName = fileName.substring(0, 200);
|
||||
//
|
||||
// // مسیر ذخیره
|
||||
// java.nio.file.Path baseDir = java.nio.file.Paths.get("uploads").toAbsolutePath().normalize();
|
||||
// java.nio.file.Files.createDirectories(baseDir);
|
||||
// String kind = "IMAGE".equalsIgnoreCase(messageType) ? "images" : "audios";
|
||||
// String subdir = kind + "/" + java.time.LocalDate.now();
|
||||
// java.nio.file.Path dir = baseDir.resolve(subdir).normalize();
|
||||
// java.nio.file.Files.createDirectories(dir);
|
||||
//
|
||||
// String ext = guessExt(fileName, mimeType);
|
||||
// String storedName = java.util.UUID.randomUUID() + ext;
|
||||
// java.nio.file.Path target = dir.resolve(storedName).normalize();
|
||||
//
|
||||
// // دریافت بایتهای فایل
|
||||
// try (OutputStream fos = new BufferedOutputStream(java.nio.file.Files.newOutputStream(
|
||||
// target, java.nio.file.StandardOpenOption.CREATE, java.nio.file.StandardOpenOption.TRUNCATE_EXISTING))) {
|
||||
// long remaining = contentLen;
|
||||
// byte[] buf = new byte[8192];
|
||||
// while (remaining > 0) {
|
||||
// int toRead = (int) Math.min(buf.length, remaining);
|
||||
// int n = dis.read(buf, 0, toRead);
|
||||
// if (n == -1) throw new EOFException("stream ended early");
|
||||
// fos.write(buf, 0, n);
|
||||
// remaining -= n;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// long fileSize = java.nio.file.Files.size(target);
|
||||
// String fileUrl = "/" + subdir.replace('\\','/') + "/" + storedName;
|
||||
//
|
||||
// FileAttachment att = new FileAttachment(
|
||||
// fileUrl,
|
||||
// messageType.toUpperCase(), // IMAGE/AUDIO
|
||||
// fileName,
|
||||
// fileSize,
|
||||
// mimeType,
|
||||
// width,
|
||||
// height,
|
||||
// null, // durationSeconds
|
||||
// null // thumbnailUrl
|
||||
// );
|
||||
//
|
||||
// boolean ok = MessageDatabase.saveMessageWithOptionalAttachments(
|
||||
// messageId, senderId, receiverId, rType, text, messageType.toUpperCase(), java.util.List.of(att)
|
||||
// );
|
||||
//
|
||||
// JSONObject ack = new JSONObject()
|
||||
// .put("status", ok ? "success" : "error")
|
||||
// .put("message_id", messageId.toString())
|
||||
// .put("file_url", fileUrl)
|
||||
// .put("file_size", fileSize)
|
||||
// .put("mime_type", mimeType);
|
||||
//
|
||||
// out.println(ack.toString());
|
||||
// out.flush();
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// out.println(new JSONObject().put("status","error").put("message","exception").toString());
|
||||
// out.flush();
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
private void handleMediaFrame(DataInputStream dis, PrintWriter out) {
|
||||
try {
|
||||
// MAGIC = "MDM1"
|
||||
final int MAGIC_EXPECTED = 0x4D444D31;
|
||||
final int MAGIC_EXPECTED = 0x4D444D31; // "MDM1"
|
||||
int magic = dis.readInt();
|
||||
if (magic != MAGIC_EXPECTED) {
|
||||
out.println(new JSONObject().put("status","error").put("message","bad magic").toString());
|
||||
out.flush();
|
||||
return;
|
||||
out.println(new JSONObject().put("status","error").put("message","bad magic").toString()); out.flush(); return;
|
||||
}
|
||||
|
||||
int headerLen = dis.readInt();
|
||||
if (headerLen <= 0 || headerLen > (64 * 1024)) {
|
||||
out.println(new JSONObject().put("status","error").put("message","bad header length").toString());
|
||||
out.flush();
|
||||
return;
|
||||
if (headerLen <= 0 || headerLen > 64 * 1024) {
|
||||
out.println(new JSONObject().put("status","error").put("message","bad header length").toString()); out.flush(); return;
|
||||
}
|
||||
|
||||
byte[] headerBytes = dis.readNBytes(headerLen);
|
||||
if (headerBytes.length != headerLen) {
|
||||
out.println(new JSONObject().put("status","error").put("message","header truncated").toString());
|
||||
out.flush();
|
||||
return;
|
||||
out.println(new JSONObject().put("status","error").put("message","header truncated").toString()); out.flush(); return;
|
||||
}
|
||||
|
||||
JSONObject h = new JSONObject(new String(headerBytes, java.nio.charset.StandardCharsets.UTF_8));
|
||||
|
||||
long contentLen = dis.readLong();
|
||||
long MAX_MEDIA = 25L * 1024 * 1024;
|
||||
if (contentLen <= 0 || contentLen > MAX_MEDIA) {
|
||||
skip(dis, contentLen);
|
||||
out.println(new JSONObject().put("status","error").put("message","file too large/invalid").toString());
|
||||
out.flush();
|
||||
return;
|
||||
out.println(new JSONObject().put("status","error").put("message","file too large/invalid").toString()); out.flush(); return;
|
||||
}
|
||||
|
||||
// الزامیها
|
||||
UUID messageId = UUID.fromString(h.getString("message_id"));
|
||||
UUID senderId = UUID.fromString(h.getString("sender_id"));
|
||||
String rType = h.getString("receiver_type"); // private/group/channel
|
||||
UUID receiverId = UUID.fromString(h.getString("receiver_id"));
|
||||
String messageType = h.getString("message_type"); // IMAGE | AUDIO
|
||||
UUID messageId = UUID.fromString(h.getString("message_id"));
|
||||
UUID senderId = UUID.fromString(h.getString("sender_id"));
|
||||
String rType = h.getString("receiver_type"); // private/group/channel
|
||||
UUID receiverId = UUID.fromString(h.getString("receiver_id"));
|
||||
String messageType = h.getString("message_type").toUpperCase(); // IMAGE | AUDIO
|
||||
|
||||
if (!"IMAGE".equalsIgnoreCase(messageType) && !"AUDIO".equalsIgnoreCase(messageType)) {
|
||||
if (!"IMAGE".equals(messageType) && !"AUDIO".equals(messageType)) {
|
||||
skip(dis, contentLen);
|
||||
out.println(new JSONObject().put("status","error").put("message","unsupported message_type").toString());
|
||||
out.flush();
|
||||
return;
|
||||
out.println(new JSONObject().put("status","error").put("message","unsupported message_type").toString()); out.flush(); return;
|
||||
}
|
||||
|
||||
String fileName = h.optString("file_name", "file.bin");
|
||||
String mimeType = h.optString("mime_type", "application/octet-stream");
|
||||
String text = h.optString("text", "");
|
||||
String text = h.optString("text", ""); // کپشن اختیاری
|
||||
|
||||
Integer width = h.has("width") && !h.isNull("width") ? h.getInt("width") : null;
|
||||
Integer height = h.has("height") && !h.isNull("height") ? h.getInt("height") : null;
|
||||
Integer width = h.has("width") && !h.isNull("width") ? h.getInt("width") : null;
|
||||
Integer height = h.has("height") && !h.isNull("height") ? h.getInt("height") : null;
|
||||
|
||||
if (fileName.length() > 200) fileName = fileName.substring(0, 200);
|
||||
|
||||
// مسیر ذخیره
|
||||
// مسیر ذخیره (فیزیکی)
|
||||
java.nio.file.Path baseDir = java.nio.file.Paths.get("uploads").toAbsolutePath().normalize();
|
||||
java.nio.file.Files.createDirectories(baseDir);
|
||||
String kind = "IMAGE".equalsIgnoreCase(messageType) ? "images" : "audios";
|
||||
String kind = "IMAGE".equals(messageType) ? "images" : "audios";
|
||||
String subdir = kind + "/" + java.time.LocalDate.now();
|
||||
java.nio.file.Path dir = baseDir.resolve(subdir).normalize();
|
||||
java.nio.file.Files.createDirectories(dir);
|
||||
@@ -2679,7 +2791,7 @@ public class ClientHandler implements Runnable {
|
||||
String storedName = java.util.UUID.randomUUID() + ext;
|
||||
java.nio.file.Path target = dir.resolve(storedName).normalize();
|
||||
|
||||
// دریافت بایتهای فایل
|
||||
// دریافت باینری فایل
|
||||
try (OutputStream fos = new BufferedOutputStream(java.nio.file.Files.newOutputStream(
|
||||
target, java.nio.file.StandardOpenOption.CREATE, java.nio.file.StandardOpenOption.TRUNCATE_EXISTING))) {
|
||||
long remaining = contentLen;
|
||||
@@ -2694,30 +2806,52 @@ public class ClientHandler implements Runnable {
|
||||
}
|
||||
|
||||
long fileSize = java.nio.file.Files.size(target);
|
||||
String fileUrl = "/" + subdir.replace('\\','/') + "/" + storedName;
|
||||
|
||||
FileAttachment att = new FileAttachment(
|
||||
fileUrl,
|
||||
messageType.toUpperCase(), // IMAGE/AUDIO
|
||||
fileName,
|
||||
fileSize,
|
||||
mimeType,
|
||||
width,
|
||||
height,
|
||||
null, // durationSeconds
|
||||
null // thumbnailUrl
|
||||
);
|
||||
String storagePath = target.toString(); // فقط سرور استفاده کنه
|
||||
String fileUrl = "/" + subdir.replace('\\','/') + "/" + storedName; // اختیاری/نمایشی (HTTP لازم نیست)
|
||||
String mt = messageType; // "IMAGE" یا "AUDIO"
|
||||
int safeWidth = ("IMAGE".equals(mt) && width != null) ? width : 0;
|
||||
int safeHeight = ("IMAGE".equals(mt) && height != null) ? height : 0;
|
||||
FileAttachment att = new FileAttachment();
|
||||
att.setFileUrl(fileUrl); // اختیاری
|
||||
att.setFileType(messageType); // IMAGE/AUDIO
|
||||
att.setFileName(fileName);
|
||||
att.setFileSize(fileSize);
|
||||
att.setMimeType(mimeType);
|
||||
att.setWidth(safeWidth);
|
||||
att.setHeight(safeHeight);
|
||||
att.setDurationSeconds(0);
|
||||
att.setThumbnailUrl(null);
|
||||
att.setStoragePath(storagePath); // اجباری برای سوکت
|
||||
// اجازه بده insertAttachmentsTx برایش mediaKey و attachmentId بسازد
|
||||
java.util.List<FileAttachment> atts = java.util.List.of(att);
|
||||
|
||||
boolean ok = MessageDatabase.saveMessageWithOptionalAttachments(
|
||||
messageId, senderId, receiverId, rType, text, messageType.toUpperCase(), java.util.List.of(att)
|
||||
messageId, senderId, receiverId, rType, text, messageType, atts
|
||||
);
|
||||
|
||||
UUID mediaKey = null;
|
||||
try (PreparedStatement q = ConnectionDb.connect().prepareStatement(
|
||||
"SELECT media_key FROM message_attachments WHERE message_id = ? AND storage_path = ? LIMIT 1"
|
||||
)) {
|
||||
q.setObject(1, messageId);
|
||||
q.setString(2, storagePath);
|
||||
try (ResultSet rs = q.executeQuery()) {
|
||||
if (rs.next()) mediaKey = (UUID) rs.getObject(1);
|
||||
}
|
||||
} catch (SQLException sqle) {
|
||||
// در بدترین حالت بدون media_key ACK میدیم، ولی بهتره خطا رو لاگ کنیم
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
|
||||
JSONObject ack = new JSONObject()
|
||||
.put("status", ok ? "success" : "error")
|
||||
.put("message_id", messageId.toString())
|
||||
.put("file_url", fileUrl)
|
||||
.put("media_key", mediaKey != null ? mediaKey.toString() : JSONObject.NULL) // برای دانلود سوکتی
|
||||
.put("file_name", fileName)
|
||||
.put("file_size", fileSize)
|
||||
.put("mime_type", mimeType);
|
||||
.put("mime_type", mimeType)
|
||||
.put("display_path", fileUrl); // صرفاً نمایشی
|
||||
|
||||
out.println(ack.toString());
|
||||
out.flush();
|
||||
@@ -2729,6 +2863,7 @@ public class ClientHandler implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void skip(DataInputStream dis, long n) throws IOException {
|
||||
if (n <= 0) return;
|
||||
byte[] buf = new byte[8192];
|
||||
@@ -2997,7 +3132,6 @@ public class ClientHandler implements Runnable {
|
||||
.put("data", chatUpdate);
|
||||
for (UUID u : allMembers) RealTimeEventDispatcher.sendToUser(u, chatPayload);
|
||||
|
||||
// حالا new_message را فقط به غیر از sender
|
||||
List<UUID> others = new ArrayList<>(allMembers);
|
||||
others.remove(senderId);
|
||||
RealTimeEventDispatcher.broadcastToUsers(others, payload);
|
||||
|
||||
Reference in New Issue
Block a user