package com.doumee.jtt808.web.service; import io.netty.buffer.ByteBuf; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.yzh.commons.util.DateUtils; import org.yzh.commons.util.IOUtils; import org.yzh.commons.util.StrUtils; import org.yzh.protocol.jsatl12.DataPacket; import org.yzh.protocol.jsatl12.T1210; import org.yzh.protocol.jsatl12.T1211; import org.yzh.protocol.t808.T0200; import org.yzh.protocol.t808.T0801; import com.doumee.jtt808.web.model.entity.DeviceDO; import com.doumee.jtt808.web.model.enums.SessionKey; import java.io.*; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; @Service public class FileService { private static final Logger log = LoggerFactory.getLogger(FileService.class.getSimpleName()); private static final Comparator comparator = Comparator.comparingLong((long[] a) -> a[0]).thenComparingLong(a -> a[1]); @Value("${jt-server.alarm-file.path}") private String workDirPath; @Value("${jt-server.jt808.media-file.path}") private String mediaFileRoot; private String getDir(T1210 alarmId) { StringBuilder sb = new StringBuilder(80); sb.append(workDirPath).append('/'); sb.append(alarmId.getClientId()).append('_'); DateUtils.yyMMddHHmmss.formatTo(alarmId.getDateTime(), sb); sb.append('_').append(alarmId.getSerialNo()).append('/'); return sb.toString(); } /** 创建报警目录及 附件列表日志 */ public void createDir(T1210 alarmId) { String dirPath = getDir(alarmId); new File(dirPath).mkdirs(); List items = alarmId.getItems(); StringBuilder fileList = new StringBuilder(items.size() * 50); fileList.append(dirPath).append(IOUtils.Separator); for (T1210.Item item : items) fileList.append(item.getName()).append('\t').append(item.getSize()).append(IOUtils.Separator); IOUtils.write(fileList.toString(), new File(dirPath, "fs.txt")); } /** 将数据块写入到报警文件,并记录日志 */ public void writeFile(T1210 alarmId, DataPacket fileData) { String dir = getDir(alarmId); String name = dir + fileData.getName().trim(); int offset = fileData.getOffset(); int length = fileData.getLength(); byte[] buffer = ByteBuffer.allocate(8) .putInt(offset).putInt(length).array(); RandomAccessFile file = null; FileOutputStream filelog = null; ByteBuf data = fileData.getData(); try { file = new RandomAccessFile(name + ".tmp", "rw"); filelog = new FileOutputStream(name + ".log", true); data.readBytes(file.getChannel(), offset, data.readableBytes()); filelog.write(buffer); } catch (IOException e) { log.error("写入报警文件", e); } finally { IOUtils.close(file, filelog); } } public void writeFileSingle(T1210 alarmId, DataPacket fileData) { String dir = getDir(alarmId); String name = dir + fileData.getName().trim(); int offset = fileData.getOffset(); RandomAccessFile file = null; ByteBuf data = fileData.getData(); try { file = new RandomAccessFile(name, "rw"); data.readBytes(file.getChannel(), offset, data.readableBytes()); } catch (IOException e) { log.error("写入报警文件", e); } finally { IOUtils.close(file); } } /** 根据日志检查文件完整性,并返回缺少的数据块信息 */ public int[] checkFile(T1210 alarmId, T1211 fileInfo) { String dir = getDir(alarmId); File logFile = new File(dir + fileInfo.getName() + ".log"); byte[] bytes; FileInputStream in = null; try { in = new FileInputStream(logFile); bytes = new byte[in.available()]; in.read(bytes); } catch (FileNotFoundException e) { return null; } catch (IOException e) { log.error("检查文件完整性", e); return null; } finally { IOUtils.close(in); } int size = bytes.length / 8; long[][] items = new long[size + 2][2]; items[size + 1][0] = fileInfo.getSize(); ByteBuffer buffer = ByteBuffer.wrap(bytes); for (int i = 1; i <= size; i++) { items[i][0] = buffer.getInt(); items[i][1] = buffer.getInt(); } List result = new ArrayList<>(items.length); int len = items.length - 1; Arrays.sort(items, 1, len, comparator); for (int i = 0; i < len; ) { long a = items[i][0] + items[i][1]; long b = items[++i][0] - a; if (b > 0) { result.add((int) a); result.add((int) b); } } if (result.isEmpty()) { File file = new File(dir + fileInfo.getName() + ".tmp"); File dest = new File(dir + fileInfo.getName()); if (file.renameTo(dest)) { logFile.delete(); } return null; } return StrUtils.toArray(result); } /** 多媒体数据上传 */ public boolean saveMediaFile(T0801 message) { DeviceDO device = SessionKey.getDevice(message.getSession()); T0200 location = message.getLocation(); StringBuilder filename = new StringBuilder(32); filename.append(type(message.getType())).append('_'); DateUtils.yyMMddHHmmss.formatTo(location.getDeviceTime(), filename); filename.append('_'); filename.append(message.getChannelId()).append('_'); filename.append(message.getEvent()).append('_'); filename.append(message.getId()).append('.'); filename.append(suffix(message.getFormat())); String deviceId; if (device == null) deviceId = message.getClientId(); else deviceId = device.getDeviceId(); File dir = new File(mediaFileRoot + '/' + deviceId); dir.mkdirs(); ByteBuf packet = message.getPacket(); FileOutputStream fos = null; try { fos = new FileOutputStream(new File(dir, filename.toString())); packet.readBytes(fos.getChannel(), 0, packet.readableBytes()); return true; } catch (IOException e) { log.error("多媒体数据保存失败", e); return false; } finally { IOUtils.close(fos); packet.release(); } } private static String type(int type) { switch (type) { case 0: return "image"; case 1: return "audio"; case 2: return "video"; default: return "unknown"; } } private static String suffix(int format) { switch (format) { case 0: return "jpg"; case 1: return "tif"; case 2: return "mp3"; case 3: return "wav"; case 4: return "wmv"; default: return "bin"; } } }