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<long[]> 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<T1210.Item> 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<Integer> 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"; 
 | 
        } 
 | 
    } 
 | 
} 
 |