服务端的TigerApi 框架,基于.NET6 2024 版本
Rodney Chen
2024-09-29 564d1fcca01d3c528e283c9feef3ea1a05140e17
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Rhea.Common;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using Tiger.IBusiness;
 
namespace Tiger.Api.Controllers.Base
{
    /// <summary>
    /// Api自动更新服务
    /// </summary>
    [Route("api/[controller]/[action]")]
    [EnableCors("Any")]
    [ApiController]
    public partial class UpgradeController : ControllerBase
    {
        /// <summary>
        /// 获取文件列表
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> GetFileListAsync(string? path)
        {
            var filePath = UpgradeFileSystem.UpgradeRootPath + path;
            var allFiles = new List<UpgradeFileSystem>();
            await Task.Run(() =>
            {
                if (Directory.Exists(filePath))
                {
                    allFiles = new UpgradeFileSystem(new DirectoryInfo(filePath)).ChildNodes;
                }
                else if (System.IO.File.Exists(filePath))
                {
                    allFiles.Add(new UpgradeFileSystem(new FileInfo(filePath)));
                }
            });
            return Ok(allFiles);
        }
 
        /// <summary>
        /// 获取文件
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> GetFileAsync(string? path)
        {
            var filePath = UpgradeFileSystem.UpgradeRootPath + path;
            if (System.IO.File.Exists(filePath))
            {
                return await GetFileStreamResultAsync(new FileInfo(filePath));
                //return await SegmentDownloadFileAsync(new FileInfo(filePath));
            }
            return Ok(null);
        }
 
        /// <summary>
        /// 获取..\AutoUpgrade.exe
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> GetAutoUpgradeProgramAsync()
        {
            var filePath = UpgradeFileSystem.UpgradeRootPath + "AutoUpgrade.exe";
            if (System.IO.File.Exists(filePath))
            {
                return await GetFileStreamResultAsync(new FileInfo(filePath));
            }
            return Ok($"请先把AutoUpgrade.exe放到更新目录的[{UpgradeFileSystem.UpgradeRootPath}]根目录下");
        }
 
        /// <summary>
        /// 获取..\AutoUpgrade.exe.config
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> GetAutoUpgradeConfigAsync()
        {
            var filePath = UpgradeFileSystem.UpgradeRootPath + "AutoUpgrade.exe.config";
            if (System.IO.File.Exists(filePath))
            {
                return await GetFileStreamResultAsync(new FileInfo(filePath));
            }
            return Ok($"请先把AutoUpgrade.exe.config放到更新目录的[{UpgradeFileSystem.UpgradeRootPath}]根目录下");
        }
 
        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="filePath"></param>
        /// <returns></returns>
        [HttpPost]
        public JsonResult UploadFile([FromForm] string fileName, [FromForm] string filePath)
        {
            var files = Request.Form.Files;
            var msgResult = new List<string>();
            if (files.Count > 0)
            {
                foreach (var formFile in files)
                {
                    var fullPath = $"{filePath}\\{fileName}";
                    //save
                    using var stream = new FileStream(fullPath, FileMode.Create);
                    formFile.CopyTo(stream);
                    stream.Close();
                    stream.Dispose();
                    //unzip
                    var unZipDir = fullPath.Replace(Path.GetFileName(fullPath), "").Trim('\\');
                    //System.IO.Compression.ZipFile.ExtractToDirectory(fullPath, unZipDir);
                    var isOK = UnZip(fullPath, unZipDir);
                    //delete compress package
                    System.IO.File.Delete(fullPath);
                    msgResult.Add($"【{fileName}】上传成功,{(isOK ? "解压成功" : "解压失败")}");
                }
            }
            return new JsonResult(msgResult);
        }
 
        #region Function
        /// <summary>
        /// 获取到文件流结果
        /// </summary>
        /// <param name="fileInfo">FileInfo文件</param>
        /// <returns>返回FileStreamResult</returns>
        private async Task<IActionResult> GetFileStreamResultAsync(FileInfo fileInfo)
        {
            if (fileInfo == null) return NotFound();
 
            var memoryStream = new MemoryStream();
            using (var stream = new FileStream(fileInfo.FullName, FileMode.Open))
            {
                await stream.CopyToAsync(memoryStream);
            }
            memoryStream.Seek(0, SeekOrigin.Begin);
 
            //文件名必须编码,否则会有特殊字符(如中文)无法在此下载。
            string encodeFilename = System.Net.WebUtility.UrlEncode(fileInfo.Name);
            Response.Headers.Add("Content-Disposition", "attachment; filename=" + encodeFilename);
            return new FileStreamResult(memoryStream, "application/octet-stream");//文件流方式,指定文件流对应的ContenType。
        }
 
        /// <summary>
        /// 获取到文件流结果
        /// </summary>
        /// <param name="fileInfo">FileInfo文件</param>
        /// <returns>返回FileStreamResult</returns>
        private IActionResult GetFileStreamResult(FileInfo fileInfo)
        {
            if (fileInfo == null) return NotFound();
 
            var memoryStream = new MemoryStream();
            using (var stream = new FileStream(fileInfo.FullName, FileMode.Open))
            {
                stream.CopyTo(memoryStream);
            }
            memoryStream.Seek(0, SeekOrigin.Begin);
 
            //文件名必须编码,否则会有特殊字符(如中文)无法在此下载。
            string encodeFilename = System.Net.WebUtility.UrlEncode(fileInfo.Name);
            Response.Headers.Add("Content-Disposition", "attachment; filename=" + encodeFilename);
            return new FileStreamResult(memoryStream, "application/octet-stream");//文件流方式,指定文件流对应的ContenType。
        }
 
        /// <summary>
        /// 异步分段下载数据
        /// </summary>
        /// <param name="fileInfo">FileInfo文件</param>
        /// <returns>返回响应结果</returns>
        private async Task<IActionResult> SegmentDownloadFileAsync(FileInfo fileInfo)
        {
            if (fileInfo == null) return NotFound();
 
            int index = 0;
 
            using (FileStream fs = new FileStream(fileInfo.FullName, FileMode.Open))
            {
                if (fs.Length <= 0)
                {
                    return Ok(new { code = -1, msg = "文件尚未处理" });
                }
                int shardSize = 1 * 1024 * 1024;//一次1M
                int count = (int)(fs.Length / shardSize);
 
                if ((fs.Length % shardSize) > 0)
                {
                    count += 1;
                }
                if (index > count - 1)
                {
                    return Ok(new { code = -1, msg = "无效的下标" });
                }
 
                fs.Seek(index * shardSize, SeekOrigin.Begin);
 
                if (index == count - 1)
                {
                    //最后一片 = 总长 - (每次片段大小 * 已下载片段个数)
                    shardSize = (int)(fs.Length - (shardSize * index));
                }
                byte[] datas = new byte[shardSize];
                await fs.ReadAsync(datas.AsMemory(0, datas.Length));
 
                return File(datas, "application/octet-stream");
            }
        }
 
        /// <summary>
        /// 分段下载数据
        /// </summary>
        /// <param name="fileInfo">FileInfo文件</param>
        /// <returns>返回响应结果</returns>
        private IActionResult SegmentDownloadFile(FileInfo fileInfo)
        {
            if (fileInfo == null) return NotFound();
 
            int index = 0;
 
            using (FileStream fs = new FileStream(fileInfo.FullName, FileMode.Open))
            {
                if (fs.Length <= 0)
                {
                    return Ok(new { code = -1, msg = "文件尚未处理" });
                }
                int shardSize = 1 * 1024 * 1024;//一次1M
                int count = (int)(fs.Length / shardSize);
 
                if ((fs.Length % shardSize) > 0)
                {
                    count += 1;
                }
                if (index > count - 1)
                {
                    return Ok(new { code = -1, msg = "无效的下标" });
                }
 
                fs.Seek(index * shardSize, SeekOrigin.Begin);
 
                if (index == count - 1)
                {
                    //最后一片 = 总长 - (每次片段大小 * 已下载片段个数)
                    shardSize = (int)(fs.Length - (shardSize * index));
                }
                byte[] datas = new byte[shardSize];
                fs.Read(datas, 0, datas.Length);
 
                return File(datas, "application/octet-stream");
            }
        }
        /// <summary>  
        /// 功能:解压zip格式的文件。  
        /// </summary>  
        /// <param name="zipFilePath">压缩文件路径</param>  
        /// <param name="unZipDir">解压文件存放路径,为空时默认与压缩文件同一级目录下,跟压缩文件同名的文件夹</param>  
        /// <returns>解压是否成功</returns>  
        private bool UnZip(string zipFilePath, string unZipDir)
        {
            try
            {
                if (zipFilePath == string.Empty)
                {
                    throw new Exception("压缩文件不能为空!");
                }
                if (!System.IO.File.Exists(zipFilePath))
                {
                    throw new FileNotFoundException("压缩文件不存在!");
                }
                //解压文件夹为空时默认与压缩文件同一级目录下,跟压缩文件同名的文件夹  
                if (unZipDir == string.Empty)
                    unZipDir = zipFilePath.Replace(Path.GetFileName(zipFilePath), Path.GetFileNameWithoutExtension(zipFilePath));
                if (!unZipDir.EndsWith("/"))
                    unZipDir += "/";
                if (!Directory.Exists(unZipDir))
                    Directory.CreateDirectory(unZipDir);
                using (var s = new ZipInputStream(System.IO.File.OpenRead(zipFilePath)))
                {
 
                    ZipEntry theEntry;
                    while ((theEntry = s.GetNextEntry()) != null)
                    {
                        string directoryName = Path.GetDirectoryName(theEntry.Name);
                        string fileName = Path.GetFileName(theEntry.Name);
                        if (!string.IsNullOrEmpty(directoryName))
                        {
                            Directory.CreateDirectory(unZipDir + directoryName);
                        }
                        if (directoryName != null && !directoryName.EndsWith("/"))
                        {
                        }
                        if (fileName != string.Empty)
                        {
                            using FileStream streamWriter = System.IO.File.Create(unZipDir + theEntry.Name);
 
                            int size;
                            byte[] data = new byte[2048];
                            while (true)
                            {
                                size = s.Read(data, 0, data.Length);
                                if (size > 0)
                                {
                                    streamWriter.Write(data, 0, size);
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                //_logger.LogError(ex, $"解压zip格式的文件失败:{zipFilePath}");
                return false;
            }
        }
 
 
        #endregion
 
    }//endClass
 
}