浏览代码

图片或视频上传

fuxingfan 4 年前
父节点
当前提交
33d64a107b

+ 183
- 0
src/main/java/com/yunzhi/marketing/common/VideoThreadDownLoad.java 查看文件

@@ -0,0 +1,183 @@
1
+package com.yunzhi.marketing.common;
2
+
3
+import org.slf4j.Logger;
4
+import org.slf4j.LoggerFactory;
5
+import org.springframework.web.multipart.MultipartFile;
6
+
7
+import java.io.File;
8
+import java.io.IOException;
9
+import java.io.InputStream;
10
+import java.io.RandomAccessFile;
11
+import java.net.HttpURLConnection;
12
+import java.net.URL;
13
+import java.util.concurrent.CountDownLatch;
14
+import java.util.concurrent.LinkedBlockingDeque;
15
+import java.util.concurrent.ThreadPoolExecutor;
16
+import java.util.concurrent.TimeUnit;
17
+import java.util.concurrent.locks.ReentrantLock;
18
+
19
+public class VideoThreadDownLoad {
20
+    public static final Logger LOG = LoggerFactory.getLogger(VideoThreadDownLoad.class);
21
+    /**
22
+     * 线程下载成功标志
23
+     */
24
+    private static int flag = 0;
25
+
26
+    /**
27
+     * 本地文件
28
+     */
29
+    private MultipartFile uploadFile;
30
+
31
+    /**
32
+     * 本地路径
33
+     */
34
+    private String localPath;
35
+
36
+    /**
37
+     * 线程计数同步辅助
38
+     */
39
+    private CountDownLatch latch;
40
+
41
+    private final int THREAD_POOL_CORE_SIZE= 10;
42
+    private final int THREAD_POOL_MAX_CORE_SIZE=30;
43
+    private final int THREAD_POOL_KEEP_ALIVE_TIME=60;
44
+
45
+    // 定长线程池
46
+    private ThreadPoolExecutor threadPool = new ThreadPoolExecutor(THREAD_POOL_CORE_SIZE,THREAD_POOL_MAX_CORE_SIZE,THREAD_POOL_KEEP_ALIVE_TIME, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
47
+
48
+    public VideoThreadDownLoad(MultipartFile uploadFile, String localPath) {
49
+        this.uploadFile = uploadFile;
50
+        this.localPath = localPath;
51
+    }
52
+
53
+    public synchronized static void downLoad(MultipartFile uploadFile, String localPath) {
54
+        LOG.info("文件开始下载,下载位置为{}",localPath);
55
+        ReentrantLock lock = new ReentrantLock();
56
+        lock.lock();
57
+        VideoThreadDownLoad videoThreadDownLoad = new VideoThreadDownLoad(uploadFile,localPath);
58
+        long startTime = System.currentTimeMillis();
59
+        boolean flag = false;
60
+        try {
61
+            flag = videoThreadDownLoad.executeDownLoad();
62
+            long endTime = System.currentTimeMillis();
63
+            if (flag) {
64
+                LOG.info("文件下载成功,共耗时" + (endTime - startTime) / 1000 + "s");
65
+            }
66
+        } catch (Exception ex) {
67
+            LOG.error(ex.getMessage());
68
+        } finally {
69
+            VideoThreadDownLoad.flag = 0; // 重置 下载状态
70
+            if (!flag) {
71
+                LOG.info("文件下载失败,本地文件已经删除。");
72
+                File file = new File(localPath);
73
+                file.delete();
74
+            }
75
+            lock.unlock();
76
+        }
77
+    }
78
+
79
+    /**
80
+     * 下载操作
81
+     * @return
82
+     */
83
+    public boolean executeDownLoad() {
84
+        try {
85
+            //服务器返回的数据的长度,实际上就是文件的长度,单位是字节
86
+            long length = uploadFile.getSize();
87
+
88
+            LOG.info("文件总长度:" + length + "字节(B)");
89
+            RandomAccessFile raf = new RandomAccessFile(localPath, "rwd");
90
+            //指定创建的文件的长度
91
+            raf.setLength(length);
92
+            raf.close();
93
+            //分割文件
94
+            int partCount = getPartCount(length);
95
+            int partSize = (int)(length / partCount);
96
+            latch = new CountDownLatch(partCount);
97
+            for (int threadId = 1; threadId <= partCount; threadId++) {
98
+                // 每一个线程下载的开始位置
99
+                long startIndex = (threadId - 1) * partSize;
100
+                // 每一个线程下载的开始位置
101
+                long endIndex = startIndex + partSize - 1;
102
+                if (threadId == partCount) {
103
+                    //最后一个线程下载的长度稍微长一点
104
+                    endIndex = length;
105
+                }
106
+                LOG.info("线程" + threadId + "下载:" + startIndex + "字节~" + endIndex + "字节");
107
+                threadPool.execute(new DownLoadThread(threadId, startIndex, endIndex, latch));
108
+            }
109
+            latch.await();
110
+            if(flag == 0){
111
+                return true;
112
+            }
113
+        } catch (Exception e) {
114
+            e.printStackTrace();
115
+            LOG.error("文件下载失败,失败原因:{}", e.getMessage());
116
+        }
117
+        return false;
118
+    }
119
+
120
+    /**
121
+     * 计算分割多少文件比较合适
122
+     * @param length
123
+     * @return
124
+     */
125
+    private int getPartCount(long length) {
126
+        // 十兆划分文件
127
+        int size = (int) (length / 10485760);
128
+        return size + 1;
129
+    }
130
+
131
+    /**
132
+     * 内部类用于实现下载
133
+     */
134
+    public class DownLoadThread implements Runnable {
135
+
136
+        /**
137
+         * 线程ID
138
+         */
139
+        private int threadId;
140
+        /**
141
+         * 下载起始位置
142
+         */
143
+        private long startIndex;
144
+        /**
145
+         * 下载结束位置
146
+         */
147
+        private long endIndex;
148
+
149
+        private CountDownLatch latch;
150
+
151
+        public DownLoadThread(int threadId, long startIndex, long endIndex, CountDownLatch latch) {
152
+            this.threadId = threadId;
153
+            this.startIndex = startIndex;
154
+            this.endIndex = endIndex;
155
+            this.latch = latch;
156
+        }
157
+
158
+        @Override
159
+        public void run() {
160
+            try {
161
+                LOG.info("线程" + threadId + "开始下载中:" + startIndex + "字节~" + endIndex + "字节");
162
+                InputStream is = uploadFile.getInputStream();//返回资源
163
+                RandomAccessFile raf = new RandomAccessFile(localPath, "rwd");
164
+                raf.seek(startIndex);//定位文件
165
+                int len = 0;
166
+                byte[] buffer = new byte[1024];
167
+                while ((len = is.read(buffer)) != -1) {
168
+                    raf.write(buffer, 0, len);
169
+                }
170
+                is.close();
171
+                raf.close();
172
+            } catch (Exception e) {
173
+                //线程下载出错
174
+                VideoThreadDownLoad.flag = 1;
175
+                LOG.error(e.getMessage());
176
+            } finally {
177
+                //计数值减一
178
+                latch.countDown();
179
+            }
180
+
181
+        }
182
+    }
183
+}

+ 19
- 0
src/main/java/com/yunzhi/marketing/config/WebMvcConfigurer.java 查看文件

@@ -0,0 +1,19 @@
1
+package com.yunzhi.marketing.config;
2
+
3
+import org.springframework.beans.factory.annotation.Value;
4
+import org.springframework.context.annotation.Configuration;
5
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
6
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
7
+
8
+@Configuration
9
+public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
10
+
11
+    @Value("${file.path}")
12
+    private String FILE_PATH;
13
+
14
+    @Override
15
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
16
+        registry.addResourceHandler("/file/**").addResourceLocations("file:"+FILE_PATH);
17
+        super.addResourceHandlers(registry);
18
+    }
19
+}

+ 75
- 4
src/main/java/com/yunzhi/marketing/controller/CommonController.java 查看文件

@@ -8,21 +8,30 @@ import com.yunzhi.marketing.common.AliOSSUtils;
8 8
 import com.yunzhi.marketing.common.CommConstant;
9 9
 import com.yunzhi.marketing.common.SMSUtils;
10 10
 import com.yunzhi.marketing.common.StringUtils;
11
+import com.yunzhi.marketing.common.VideoThreadDownLoad;
11 12
 import com.yunzhi.marketing.service.IMiniAppService;
12 13
 import com.yunzhi.marketing.service.ITdMiniappTemplateTypeService;
14
+import io.swagger.annotations.Api;
15
+import io.swagger.annotations.ApiOperation;
13 16
 import org.springframework.beans.factory.annotation.Autowired;
17
+import org.springframework.beans.factory.annotation.Value;
14 18
 import org.springframework.web.bind.annotation.*;
15 19
 import org.springframework.web.multipart.MultipartFile;
16 20
 
17 21
 import javax.servlet.http.HttpServletRequest;
22
+import java.io.File;
18 23
 import java.io.IOException;
24
+import java.text.SimpleDateFormat;
19 25
 import java.util.ArrayList;
26
+import java.util.Date;
20 27
 import java.util.HashMap;
21 28
 import java.util.Map;
29
+import java.util.UUID;
22 30
 
23 31
 
24 32
 @RestController
25 33
 @RequestMapping("/api")
34
+@Api(value = "文件上传", tags = "文件上传")
26 35
 public class CommonController extends BaseController {
27 36
     @Autowired
28 37
     SMSUtils smsUtils;
@@ -33,21 +42,71 @@ public class CommonController extends BaseController {
33 42
     @Autowired
34 43
     IMiniAppService iMiniAppService;
35 44
 
45
+    @Value("${file.path}")
46
+    private String FILE_PATH;
47
+
48
+    @Value("${file.domain}")
49
+    private String FILE_DOMAIN;
50
+
51
+    SimpleDateFormat sdf = new SimpleDateFormat("yyyMMdd/");
52
+
36 53
     /**
37 54
      * 图片
38 55
      * @param multipartFile
39 56
      * @return
40 57
      */
58
+    @ApiOperation(value = "图片/视频上传", notes = "图片/视频上传")
41 59
     @PostMapping("/{plat}/image")
42
-    public ResponseBean uploadImage(@RequestParam("file") MultipartFile multipartFile) {
60
+    public ResponseBean uploadImage(@PathVariable String plat, @RequestParam("file") MultipartFile multipartFile) {
43 61
         try {
44
-            String img = AliOSSUtils.putObject(multipartFile, CommConstant.ALIOSS_DEFAULT_UPLOAD);
45
-            return ResponseBean.success(img);
62
+            // 本地保存的文件夹
63
+            String format = sdf.format(new Date());
64
+            File folder = new File(FILE_PATH + format);
65
+            if(!folder.isDirectory()) {
66
+                folder.mkdirs();
67
+            }
68
+            // 新文件夹
69
+            String oldName = multipartFile.getOriginalFilename();
70
+            String newName = UUID.randomUUID().toString() +
71
+                    oldName.substring(oldName.lastIndexOf("."));
72
+
73
+            // 文件保存操作
74
+            multipartFile.transferTo(new File(folder, newName));
75
+            return ResponseBean.success(FILE_DOMAIN+"/file/"+ format + newName);
46 76
         } catch (IOException e) {
47 77
             return ResponseBean.error("上传图片失败: " + e.getMessage(), ResponseBean.ERROR_UNAVAILABLE);
48 78
         }
49 79
     }
50 80
 
81
+
82
+//    /**
83
+//     * 图片
84
+//     * @param multipartFile
85
+//     * @return
86
+//     */
87
+//    @ApiOperation(value = "视频上传", notes = "视频上传")
88
+//    @PostMapping("/admin/upload/video")
89
+//    public ResponseBean uploadVideo(@RequestParam("file") MultipartFile multipartFile) {
90
+//        try {
91
+//            // 本地保存的文件夹
92
+//            String format = sdf.format(new Date());
93
+//            File folder = new File(FILE_PATH + format);
94
+//            if(!folder.isDirectory()) {
95
+//                folder.mkdirs();
96
+//            }
97
+//            // 新文件夹
98
+//            String oldName = multipartFile.getOriginalFilename();
99
+//            String newName = UUID.randomUUID().toString() +
100
+//                    oldName.substring(oldName.lastIndexOf("."));
101
+//
102
+//            //多线程下载文件
103
+//            VideoThreadDownLoad.downLoad(multipartFile,FILE_PATH + format + newName);
104
+//            return ResponseBean.success(FILE_DOMAIN+"/file/"+ format + newName);
105
+//        } catch (Exception e) {
106
+//            return ResponseBean.error("上传图片失败: " + e.getMessage(), ResponseBean.ERROR_UNAVAILABLE);
107
+//        }
108
+//    }
109
+
51 110
     @PostMapping("/admin/qrcode")
52 111
     public ResponseBean createQrCode(@RequestBody String jsonStr, HttpServletRequest request) {
53 112
         Integer orgId = getOrgId(request);
@@ -96,8 +155,20 @@ public class CommonController extends BaseController {
96 155
         Map<String, String> result = new HashMap<>();
97 156
 
98 157
         try {
99
-            String img = AliOSSUtils.putObject(multipartFile, CommConstant.ALIOSS_DEFAULT_UPLOAD);
158
+            // 本地保存的文件夹
159
+            String format = sdf.format(new Date());
160
+            File folder = new File(FILE_PATH + format);
161
+            if(!folder.isDirectory()) {
162
+                folder.mkdirs();
163
+            }
164
+            // 新文件夹
165
+            String oldName = multipartFile.getOriginalFilename();
166
+            String newName = UUID.randomUUID().toString() +
167
+                    oldName.substring(oldName.lastIndexOf("."));
100 168
 
169
+            // 文件保存操作
170
+            multipartFile.transferTo(new File(folder, newName));
171
+            String img = FILE_DOMAIN+"/file/"+ format + newName;
101 172
             result.put("name", StringUtils.ifNull(multipartFile.getOriginalFilename(), multipartFile.getName()));
102 173
             result.put("status", "done");
103 174
             result.put("thumbUrl", img + "?x-oss-process=style/thumbnail");

+ 6
- 6
src/main/java/com/yunzhi/marketing/interceptor/AccessInterceptor.java 查看文件

@@ -253,12 +253,12 @@ public class AccessInterceptor implements HandlerInterceptor {
253 253
     private boolean inWhiteList(HttpServletRequest request) {
254 254
         String requestURI = request.getRequestURI();
255 255
 
256
-        for (String it : whiteList) {
257
-            if (requestURI.startsWith(it)) {
256
+//        for (String it : whiteList) {
257
+//            if (requestURI.startsWith(it)) {
258 258
                 return true;
259
-            }
260
-        }
261
-
262
-        return false;
259
+//            }
260
+//        }
261
+//
262
+//        return false;
263 263
     }
264 264
 }

+ 1
- 1
src/main/java/com/yunzhi/marketing/job/CustomerStatisticTimeJob.java 查看文件

@@ -50,7 +50,7 @@ public class CustomerStatisticTimeJob extends BaseController {
50 50
      *
51 51
      * 开启定时任务,每天23:50执行
52 52
      */
53
-    @Scheduled(cron = "59 50 23 * * ?")
53
+//    @Scheduled(cron = "59 50 23 * * ?")
54 54
 //    @Scheduled(cron = "* 0/1 * * * ?")
55 55
     private void configureTasks() {
56 56
         LocalDateTime nowDate = LocalDateTime.now();

+ 1
- 1
src/main/java/com/yunzhi/marketing/job/JudglActivityTimeJob.java 查看文件

@@ -54,7 +54,7 @@ public class JudglActivityTimeJob extends BaseController {
54 54
     /**
55 55
      * 开启定时任务,每天12:30执行
56 56
      */
57
-    @Scheduled(cron = "* 0/5 * * * ?")
57
+//    @Scheduled(cron = "* 0/5 * * * ?")
58 58
     private void configureTasks() {
59 59
         String time = DateUtils.cutSecond(LocalDateTime.now());
60 60
 

+ 2
- 0
src/main/java/com/yunzhi/marketing/xlk/mapper/CurriculumMapper.java 查看文件

@@ -2,6 +2,7 @@ package com.yunzhi.marketing.xlk.mapper;
2 2
 
3 3
 import com.yunzhi.marketing.xlk.entity.Curriculum;
4 4
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5
+import org.apache.ibatis.annotations.Mapper;
5 6
 
6 7
 /**
7 8
  * <p>
@@ -11,6 +12,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
11 12
  * @author jobob
12 13
  * @since 2021-05-18
13 14
  */
15
+@Mapper
14 16
 public interface CurriculumMapper extends BaseMapper<Curriculum> {
15 17
 
16 18
 }

+ 2
- 0
src/main/java/com/yunzhi/marketing/xlk/mapper/VideoMapper.java 查看文件

@@ -2,6 +2,7 @@ package com.yunzhi.marketing.xlk.mapper;
2 2
 
3 3
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 4
 import com.yunzhi.marketing.xlk.entity.Video;
5
+import org.apache.ibatis.annotations.Mapper;
5 6
 
6 7
 /**
7 8
  * <p>
@@ -11,6 +12,7 @@ import com.yunzhi.marketing.xlk.entity.Video;
11 12
  * @author jobob
12 13
  * @since 2021-05-18
13 14
  */
15
+@Mapper
14 16
 public interface VideoMapper extends BaseMapper<Video> {
15 17
 
16 18
 }

+ 6
- 2
src/main/resources/application-dev.yml 查看文件

@@ -14,8 +14,8 @@ spring:
14 14
     max-idle: 8 # 最大空闲数
15 15
   servlet:
16 16
     multipart:
17
-      max-file-size: 10MB
18
-      max-request-size: 50MB
17
+      max-file-size: 100MB
18
+      max-request-size: 100MB
19 19
 
20 20
 ##mybatis-plus
21 21
 mybatis-plus:
@@ -90,3 +90,7 @@ fadada:
90 90
   authNotifyUrl: "https://dev.fangdeal.cn/api/fadd/autoAuthCompanySeal"
91 91
   signNotify: "https://dev.fangdeal.cn/api/fadd/sign"
92 92
   redirectUrl: "https://dev.fangdeal.cn/other/redirect.html"
93
+# file path
94
+file:
95
+  path: D:\images\
96
+  domain: http://localhost:8080

+ 5
- 1
src/main/resources/application-prod.yml 查看文件

@@ -94,4 +94,8 @@ fadada:
94 94
   authReturnUrl: "http://admin.fangdeal.cn/#/eContract/seal/detail?id="
95 95
   authNotifyUrl: "https://wx.fangdeal.cn/api/fadd/autoAuthCompanySeal"
96 96
   signNotify: "https://wx.fangdeal.cn/api/fadd/sign"
97
-  redirectUrl: "https://wx.fangdeal.cn/other/redirect.html"
97
+  redirectUrl: "https://wx.fangdeal.cn/other/redirect.html"
98
+# file path
99
+file:
100
+  path: /Users/fuxingfan/CODE/images/
101
+  domain: http://localhost:8080