liusuyi
2025-12-23 c7fa563f9a951c647725c3d0e02b7bce366d23ea
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
package com.ard.work.service.impl;
 
import com.ard.common.config.OssConfiguration;
import com.ard.oss.service.impl.OssServiceContext;
import com.ard.work.config.WaylinePlaningConfig;
import com.ard.work.domain.dto.wayline.*;
import com.ard.work.domain.entity.UavWayline;
import com.ard.work.domain.enums.WaylineEnum;
import com.ard.work.mapper.UavWaylineMapper;
import com.ard.work.service.IGeoToolsService;
import com.ard.work.service.IKmzFileService;
import com.ard.work.util.Vertex;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.*;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.io.*;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
 
/**
 * @author liusuyi
 * @version 3.0
 * @date 2025/06/27
 */
@Service
@Slf4j
@Transactional
public class KmzFileServiceImpl implements IKmzFileService {
 
    @Resource
    private IGeoToolsService geoToolsService;
 
    @Resource
    private WaylinePlaningConfig waylinePlaningConfig;
 
    @Resource
    private UavWaylineMapper uavWaylineMapper;
 
    @Resource
    private OssServiceContext ossService;
 
    private Document initWayline(Boolean isTemplate) {
        //创建Document对象
        Document document = DocumentHelper.createDocument();
        document.setXMLEncoding("UTF-8");
 
        // 定义命名空间
        String KML_NAMESPACE = "http://www.opengis.net/kml/2.2";
        String WPML_NAMESPACE = "http://www.dji.com/wpmz/1.0.6";
 
        //创建kml节点
        QName kmlQName = QName.get("kml", Namespace.get("", KML_NAMESPACE));
        Element kml = document.addElement(kmlQName);
        //添加命名空间
        kml.addNamespace("wpml", WPML_NAMESPACE);
        //生成Document节点
        Element wpml = kml.addElement("Document", KML_NAMESPACE);
        //是否为模板
        if (isTemplate) {
            // Step 1: Implement File Creation Information
            //wpml.addElement("wpml:author").addText(user);
            wpml.addElement("wpml:createTime").addText(
                    String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()));
            wpml.addElement("wpml:updateTime").addText(
                    String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()));
        }
        //返回
        return document;
    }
 
    //产生kmz总体过程
    private void gntTemplateKml(ArdWaylineDTO ardWaylineDTO, String savePath) {
        //初始化文件头
        Document reader = this.initWayline(true);
        //Document元素
        Element document = reader.getRootElement().element("Document");
        //添加missionConfig信息
        this.addMissionConfig(document, ardWaylineDTO);
        //添加folder信息
        Element folder = this.addFolder(document, ardWaylineDTO);
        //2设置航点参数
        List<ArdUavPositionDTO> uavPositions = ardWaylineDTO.getUavPositions();
        //循环添加placemark
        Integer actionGroupId = 0;
        for (int i = 0; i < uavPositions.size(); i++) {
            ArdUavPositionDTO onePosition = uavPositions.get(i);
            this.addPlaceMark(folder, i, actionGroupId, onePosition);
            if (onePosition.getActionGroup().size() > 0) {
                actionGroupId += 1;
            }
        }
        //打印输出结果
        //log.info(reader.asXML());
        //输出
        String tmpKml = savePath + File.separator + "wpmz" + File.separator + "template.kml";
        this.outputXml(tmpKml, reader);
    }
 
    public void gntWaylineWpml(ArdWaylineDTO ardWaylineDTO, String savePath) {
        //初始化文件头
        Document reader = this.initWayline(false);
        //Document元素
        Element document = reader.getRootElement().element("Document");
        //添加missionConfig信息
        this.addMissionConfig(document, ardWaylineDTO);
        //添加folder信息
        Element folder = this.addFolderWayline(document, ardWaylineDTO);
        //2设置航点参数
        //循环添加placemark
        Integer actionGroupId = 0;
        for (int i = 0; i < ardWaylineDTO.getUavPositions().size(); i++) {
            ArdUavPositionDTO onePosition = ardWaylineDTO.getUavPositions().get(i);
            this.addPlaceMarkWayline(folder, i, actionGroupId, onePosition, ardWaylineDTO);
            if (onePosition.getActionGroup().size() > 0) {
                actionGroupId += 1;
            }
        }
        //打印输出结果
        //log.info(reader.asXML());
        //输出
        String waylineWpml = savePath + File.separator + "wpmz" + File.separator + "waylines.wpml";
        this.outputXml(waylineWpml, reader);
    }
 
    //missionConfig信息
    private void addMissionConfig(Element document, ArdWaylineDTO ardWaylineDTO) {
        //创建wpml:missionConfig元素
        Element missionConfig = document.addElement("wpml:missionConfig");
        //创建子元素
        missionConfig.addElement("wpml:flyToWaylineMode").addText("safely");
        missionConfig.addElement("wpml:finishAction").addText("goHome");
        missionConfig.addElement("wpml:exitOnRCLost").addText("goContinue");
        missionConfig.addElement("wpml:executeRCLostAction").addText("goBack");
        missionConfig.addElement("wpml:takeOffSecurityHeight").addText("30");
        //与第一个点重合
        ArdPositionDTO firstPoint = ardWaylineDTO.getUavPositions().get(0).getUavPosition();
        String refPoint = String.format("%s,%s,%s", firstPoint.getLatitude(), firstPoint.getLongitude()
                , firstPoint.getAltitude());
        missionConfig.addElement("wpml:takeOffRefPoint").addText(refPoint);
        missionConfig.addElement("wpml:globalTransitionalSpeed").addText(String.valueOf(ardWaylineDTO.getGlobalFlySpeed()));
        missionConfig.addElement("wpml:globalRTHHeight").addText(String.valueOf(ardWaylineDTO.getGlobalFlyHeight()));
        //添加droneInfo
        Element droneInfo = missionConfig.addElement("wpml:droneInfo");
        droneInfo.addElement("wpml:droneEnumValue").addText("89");
        droneInfo.addElement("wpml:droneSubEnumValue").addText("1");
        //添加payloadInfo
        Element payloadInfo = missionConfig.addElement("wpml:payloadInfo");
        payloadInfo.addElement("wpml:payloadEnumValue").addText("43");
        payloadInfo.addElement("wpml:payloadSubEnumValue").addText("0");
        payloadInfo.addElement("wpml:payloadPositionIndex").addText("0");
    }
 
    //folder信息
    private Element addFolder(Element document, ArdWaylineDTO ardWaylineDTO) {
        //全局信息
        Element folder = document.addElement("Folder");
        folder.addElement("wpml:templateType").addText("waypoint");
        folder.addElement("wpml:templateId").addText("0");
        //坐标系统
        Element waylineCoordinateSysParam = folder.addElement("wpml:waylineCoordinateSysParam");
        waylineCoordinateSysParam.addElement("wpml:coordinateMode").addText("WGS84");
        waylineCoordinateSysParam.addElement("wpml:heightMode").addText("relativeToStartPoint");
        //
        folder.addElement("wpml:autoFlightSpeed").addText(String.valueOf(ardWaylineDTO.getGlobalFlySpeed()));
        folder.addElement("wpml:globalWaypointTurnMode").addText("toPointAndStopWithDiscontinuityCurvature");
        folder.addElement("wpml:globalUseStraightLine").addText("1");
        //全局航向角模式
        Element globalWaypointHeadingParam = folder.addElement("wpml:globalWaypointHeadingParam");
        globalWaypointHeadingParam.addElement("wpml:waypointHeadingMode").addText("followWayline");
        globalWaypointHeadingParam.addElement("wpml:waypointHeadingYawPathMode").addText("followBadArc");
        //
        folder.addElement("wpml:gimbalPitchMode").addText("manual");//manual , usePointSetting
        folder.addElement("wpml:globalHeight").addText(String.valueOf(ardWaylineDTO.getGlobalFlyHeight()));
        //
        return folder;
    }
 
    private Element addFolderWayline(Element document, ArdWaylineDTO ardWaylineDTO) {
        //全局信息
        Element folder = document.addElement("Folder");
        folder.addElement("wpml:templateId").addText("0");
        folder.addElement("wpml:executeHeightMode").addText("relativeToStartPoint");
        folder.addElement("wpml:waylineId").addText("0");
        folder.addElement("wpml:autoFlightSpeed").addText(String.valueOf(ardWaylineDTO.getGlobalFlySpeed()));
        return folder;
    }
 
    //placemark信息
    private void addPlaceMark(Element folder, Integer index, Integer actionGroupId, ArdUavPositionDTO onePostion) {
        //添加placemark
        Element placemark = folder.addElement("Placemark");
        //转换坐标
        //航点坐标位置转换
        Double tLng = 0.0;
        Double tLat = 0.0;
        //转换坐标,将目标坐标转化为高德坐标
        if (onePostion.getUavPosition().getLongitude() != null &&
                onePostion.getUavPosition().getLatitude() != null) {
            Vertex newVertex = geoToolsService.gcj02ToTargetCoordinate(
                    new Vertex(onePostion.getUavPosition().getLongitude(),
                            onePostion.getUavPosition().getLatitude()));
            tLng = newVertex.x;
            tLat = newVertex.y;
        }
        Element point = placemark.addElement("Point");
        point.addElement("coordinates").addText(tLng + "," + tLat);
        //添加一般元素
        placemark.addElement("wpml:index").addText(String.valueOf(index));
        placemark.addElement("wpml:ellipsoidHeight").addText(String.valueOf(onePostion.getUavPosition().getAltitude()));
        placemark.addElement("wpml:height").addText(String.valueOf(onePostion.getUavPosition().getAltitude()));
        //全局信息
        placemark.addElement("wpml:useGlobalHeight").addText("1");
        placemark.addElement("wpml:useGlobalSpeed").addText("1");
        placemark.addElement("wpml:useGlobalHeadingParam").addText("1");
        placemark.addElement("wpml:useGlobalTurnParam").addText("1");
        placemark.addElement("wpml:useStraightLine").addText("1");
        //添加actionGroup
        if (onePostion.getActionGroup().size() > 0) {
            Element actionGroup = placemark.addElement("wpml:actionGroup");
            //添加动作组基础信息
            actionGroup.addElement("wpml:actionGroupId").addText(String.valueOf(actionGroupId));
            actionGroup.addElement("wpml:actionGroupStartIndex").addText(String.valueOf(index));
            actionGroup.addElement("wpml:actionGroupEndIndex").addText(String.valueOf(index));
            actionGroup.addElement("wpml:actionGroupMode").addText("sequence");
            Element actionTrigger = actionGroup.addElement("wpml:actionTrigger");
            actionTrigger.addElement("wpml:actionTriggerType").addText("reachPoint");
            //定义action
            Integer actionId = 0;
            for (int j = 0; j < onePostion.getActionGroup().size(); j++) {
                //逐个添加action
                Integer actionCount = this.addAction(actionGroup, actionId, onePostion.getActionGroup().get(j));
                actionId += actionCount;
            }
        }
    }
 
    //placemark信息
    private void addPlaceMarkWayline(Element folder, Integer index, Integer actionGroupId,
                                     ArdUavPositionDTO onePostion, ArdWaylineDTO ardWaylineDTO) {
        //添加placemark
        Element placemark = folder.addElement("Placemark");
        //转换坐标
        //航点坐标位置转换
        Double tLng = 0.0;
        Double tLat = 0.0;
        //转换坐标,将目标坐标转化为高德坐标
        if (onePostion.getUavPosition().getLongitude() != null &&
                onePostion.getUavPosition().getLatitude() != null) {
            Vertex newVertex = geoToolsService.gcj02ToTargetCoordinate(
                    new Vertex(onePostion.getUavPosition().getLongitude(),
                            onePostion.getUavPosition().getLatitude()));
            tLng = newVertex.x;
            tLat = newVertex.y;
        }
        Element point = placemark.addElement("Point");
        point.addElement("coordinates").addText(tLng + "," + tLat);
        //添加一般元素
        placemark.addElement("wpml:index").addText(String.valueOf(index));
        placemark.addElement("wpml:executeHeight").addText(String.valueOf(onePostion.getUavPosition().getAltitude()));
        placemark.addElement("wpml:waypointSpeed").addText(String.valueOf(ardWaylineDTO.getGlobalFlySpeed()));
        Element waypointHeadingParam = placemark.addElement("wpml:waypointHeadingParam");
        waypointHeadingParam.addElement("wpml:waypointHeadingMode").addText("followWayline");
        Element waypointTurnParam = placemark.addElement("wpml:waypointTurnParam");
        waypointTurnParam.addElement("wpml:waypointTurnMode").addText(WaylineEnum.getDesc(onePostion.getFlightPathMode()));
        waypointTurnParam.addElement("wpml:waypointTurnDampingDist").addText("0");
        //添加actionGroup
        if (onePostion.getActionGroup().size() > 0) {
            Element actionGroup = placemark.addElement("wpml:actionGroup");
            //添加动作组基础信息
            actionGroup.addElement("wpml:actionGroupId").addText(String.valueOf(actionGroupId));
            actionGroup.addElement("wpml:actionGroupStartIndex").addText(String.valueOf(index));
            actionGroup.addElement("wpml:actionGroupEndIndex").addText(String.valueOf(index));
            actionGroup.addElement("wpml:actionGroupMode").addText("sequence");
            Element actionTrigger = actionGroup.addElement("wpml:actionTrigger");
            actionTrigger.addElement("wpml:actionTriggerType").addText("reachPoint");
            //定义action
            Integer actionId = 0;
            for (int j = 0; j < onePostion.getActionGroup().size(); j++) {
                //逐个添加action
                Integer actionCount = this.addAction(actionGroup, actionId, onePostion.getActionGroup().get(j));
                actionId += actionCount;
            }
        }
    }
    //action
    private Integer addAction(Element actionGroup, Integer index, ArdActionDTO actionDTO) {
        Integer actionCount = 2;
        //变焦动作
        Element actionGimbal = actionGroup.addElement("wpml:action");
        actionGimbal.addElement("wpml:actionId").addText(String.valueOf(index));
        actionGimbal.addElement("wpml:actionActuatorFunc").addText("gimbalRotate");
        Element param = actionGimbal.addElement("wpml:actionActuatorFuncParam");
        param.addElement("wpml:payloadPositionIndex").addText(String.valueOf(0));
        param.addElement("wpml:gimbalHeadingYawBase").addText("north");
        param.addElement("wpml:gimbalRotateMode").addText("absoluteAngle");
        param.addElement("wpml:gimbalPitchRotateEnable").addText(String.valueOf(1));
        param.addElement("wpml:gimbalPitchRotateAngle").addText(String.valueOf(actionDTO.getActionGimbal().getPitch()));
        param.addElement("wpml:gimbalRollRotateEnable").addText(String.valueOf(0));
        param.addElement("wpml:gimbalRollRotateAngle").addText(String.valueOf(0));
        param.addElement("wpml:gimbalYawRotateEnable").addText(String.valueOf(1));
        param.addElement("wpml:gimbalYawRotateAngle").addText(String.valueOf(actionDTO.getActionGimbal().getYaw()));
        param.addElement("wpml:gimbalRotateTimeEnable").addText(String.valueOf(0));
        param.addElement("wpml:gimbalRotateTime").addText(String.valueOf(0));
        //动作类型
        switch (actionDTO.actionType) {
            case 0://拍照
                Element action0 = actionGroup.addElement("wpml:action");
                action0.addElement("wpml:actionId").addText(String.valueOf(index + 1));
                action0.addElement("wpml:actionActuatorFunc").addText("takePhoto");
                Element param0 = action0.addElement("wpml:actionActuatorFuncParam");
                param0.addElement("wpml:fileSuffix").addText("point");
                param0.addElement("wpml:payloadPositionIndex").addText(String.valueOf(0));
                break;
            case 1://录像,拆解为3个动作
                //添加开始录像动作
                Element action11 = actionGroup.addElement("wpml:action");
                action11.addElement("wpml:actionId").addText(String.valueOf(index + 1));
                action11.addElement("wpml:actionActuatorFunc").addText("startRecord");
                Element param11 = action11.addElement("wpml:actionActuatorFuncParam");
                param11.addElement("wpml:fileSuffix").addText("point");
                param11.addElement("wpml:payloadPositionIndex").addText(String.valueOf(0));
                //添加新的悬停动作
                Element action12 = actionGroup.addElement("wpml:action");
                action12.addElement("wpml:actionId").addText(String.valueOf(index + 2));
                action12.addElement("wpml:actionActuatorFunc").addText("hover");
                Element param12 = action12.addElement("wpml:actionActuatorFuncParam");
                param12.addElement("wpml:hoverTime").addText(String.valueOf(actionDTO.getActionParameter()));
                //添加结束动作
                Element action13 = actionGroup.addElement("wpml:action");
                action13.addElement("wpml:actionId").addText(String.valueOf(index + 3));
                action13.addElement("wpml:actionActuatorFunc").addText("stopRecord");
                Element param13 = action13.addElement("wpml:actionActuatorFuncParam");
                param13.addElement("wpml:payloadPositionIndex").addText(String.valueOf(0));
                actionCount = 5;
                break;
            case 2://悬停 ,协议是3,前端发送为2
                Element action2 = actionGroup.addElement("wpml:action");
                action2.addElement("wpml:actionId").addText(String.valueOf(index + 1));
                action2.addElement("wpml:actionActuatorFunc").addText("hover");
                Element param2 = action2.addElement("wpml:actionActuatorFuncParam");
                param2.addElement("wpml:hoverTime").addText(String.valueOf(actionDTO.getWaitTime()));
                break;
            default:
                actionCount = 0;
                log.info("未知的动作类型{}!", actionDTO.actionType);
                break;
        }
        return actionCount;
    }
 
    //输出模板kml
    private void outputXml(String vpath, Document doc) {
        try {
            OutputFormat format = OutputFormat.createPrettyPrint();
            format.setEncoding("UTF-8");  //FileWriter改为FileOutputStream解决乱码问题
            XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(vpath), format);
            xmlWriter.write(doc);
            xmlWriter.close();
        } catch (Exception e) {
            log.error("输出航线文件失败:{}", vpath);
            e.printStackTrace();
        }
    }
 
    //创建路径
    private String createSavePath(String waylineName) {
        //创建航线名称路径
        String pathStr = waylinePlaningConfig.getWaylineSavePath() + File.separator + waylineName;
        File savePath = new File(pathStr);
        if (!savePath.exists()) {
            savePath.mkdirs();
        } else {
            deleteFile(savePath);
            savePath.mkdirs();
        }
        //创建wpmz
        File wpmz = new File(pathStr + File.separator + "wpmz");
        wpmz.mkdir();
        //创建res
        File res = new File(pathStr + File.separator + "wpmz" + File.separator + "res");
        res.mkdir();
        return pathStr;
    }
 
    private void deleteFile(File filePath) {
        //判断路径是否存在
        if (filePath.exists()) {
            //判断是否是目录
            if (filePath.isDirectory()) {
                //将目录逐层拆分成数组元素
                File[] files = filePath.listFiles();
                for (File f : files) {
                    //递归调用,由最底层往上逐层删除
                    deleteFile(f);
                }
            }
            //如果是文件,直接删除
            filePath.delete();
        }
    }
 
    //输出MKZ过程
    @Override
    @Async
    public void outputKmz(ArdWaylineDTO ardWaylineDTO) {
        try {
            //创建目录
            String savePath = this.createSavePath(ardWaylineDTO.getWaylineName());
            //输出模板kml
            this.gntTemplateKml(ardWaylineDTO, savePath);
            //输出航线wpml
            this.gntWaylineWpml(ardWaylineDTO, savePath);
            //压缩输出成kmz
            String fileName = ardWaylineDTO.getWaylineName() + ".kmz";
            String kmzPath = waylinePlaningConfig.getWaylineSavePath() + File.separator + fileName;
            //输出kmz文件,是否成功
            Boolean isSuccess = this.toKmz(kmzPath, savePath + File.separator + "wpmz",
                    true);
            if (isSuccess) {
                //上传kmz文件到对象数据库
                InputStream inputStream = new FileInputStream(kmzPath);
                //构建记录
                String objectKey = fileName;
                String kmzUrl = ossService.upload(OssConfiguration.bucket, objectKey, inputStream);
                // 更新航线kmz地址
                UavWayline uavWayline =  UavWayline.builder()
                        .id(ardWaylineDTO.waylineId)
                        .kmzUrl(kmzUrl)
                        .build();
                uavWaylineMapper.updateUavWayline(uavWayline);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.info("生成kmz文件失败!");
        }
    }
 
    private Boolean toKmz(String zipFileName, String sourceFileName, boolean KeepDirStructure) {
        Boolean result = true;
        long start = System.currentTimeMillis();//开始
        ZipOutputStream zos = null;
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(zipFileName);
            zos = new ZipOutputStream(fileOutputStream);
            File sourceFile = new File(sourceFileName);
            compress(sourceFile, zos, sourceFile.getName(), KeepDirStructure);
            long end = System.currentTimeMillis();//结束
            System.out.println("压缩完成,耗时:" + (end - start) + " 毫秒");
        } catch (Exception e) {
            result = false;
            e.printStackTrace();
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    e.getStackTrace();
                }
            }
        }
        return result;
    }
 
    private void compress(File sourceFile, ZipOutputStream zos, String name,
                          boolean KeepDirStructure) throws Exception {
        byte[] buf = new byte[2 * 1024];
        if (sourceFile.isFile()) {
            // 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
            zos.putNextEntry(new ZipEntry(name));
            // copy文件到zip输出流中
            int len;
            FileInputStream in = new FileInputStream(sourceFile);
            while ((len = in.read(buf)) != -1) {
                zos.write(buf, 0, len);
            }
            // Complete the entry
            zos.closeEntry();
            in.close();
        } else {
            File[] listFiles = sourceFile.listFiles();
            if (listFiles == null || listFiles.length == 0) {
                // 需要保留原来的文件结构时,需要对空文件夹进行处理
                if (KeepDirStructure) {
                    // 空文件夹的处理
                    zos.putNextEntry(new ZipEntry(name + "/"));
                    // 没有文件,不需要文件的copy
                    zos.closeEntry();
                }
            } else {
                for (File file : listFiles) {
                    // 判断是否需要保留原来的文件结构
                    if (KeepDirStructure) {
                        // 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,
                        // 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了
                        compress(file, zos, name + "/" + file.getName(), KeepDirStructure);
                    } else {
                        compress(file, zos, file.getName(), KeepDirStructure);
                    }
 
                }
            }
        }
    }
 
}