44import com .alibaba .fastjson .JSON ;
55import com .xwintop .xJavaFxTool .model .PluginJarInfo ;
66import java .io .File ;
7+ import java .io .FileOutputStream ;
78import java .io .IOException ;
9+ import java .io .InputStream ;
810import java .nio .charset .Charset ;
911import java .nio .charset .StandardCharsets ;
1012import java .nio .file .Files ;
1517import java .util .List ;
1618import java .util .Objects ;
1719import java .util .concurrent .CompletableFuture ;
20+ import java .util .function .BiConsumer ;
1821import java .util .function .Consumer ;
1922import lombok .extern .slf4j .Slf4j ;
23+ import okhttp3 .Interceptor ;
24+ import okhttp3 .MediaType ;
25+ import okhttp3 .OkHttpClient ;
26+ import okhttp3 .Request .Builder ;
27+ import okhttp3 .Response ;
28+ import okhttp3 .ResponseBody ;
29+ import okio .Buffer ;
30+ import okio .BufferedSource ;
31+ import okio .ForwardingSource ;
32+ import okio .Okio ;
33+ import okio .Source ;
34+ import org .apache .commons .io .IOUtils ;
2035
2136@ Slf4j
2237public class PluginManager {
@@ -40,6 +55,9 @@ public static PluginManager getInstance() {
4055
4156 private final String localPluginsPath ;
4257
58+ private final OkHttpClient pluginDownloader =
59+ new OkHttpClient .Builder ().addInterceptor (new DownloadProgressInterceptor ()).build ();
60+
4361 private final List <PluginJarInfo > pluginList = new ArrayList <>(); // 插件列表
4462
4563 public PluginManager (String localPluginsPath ) {
@@ -163,14 +181,44 @@ public File downloadPlugin(PluginJarInfo pluginJarInfo) throws IOException {
163181 return file ;
164182 }
165183
184+ public File downloadPlugin (
185+ PluginJarInfo pluginJarInfo , BiConsumer <Long , Long > onProgressUpdate
186+ ) throws IOException {
187+
188+ PluginJarInfo plugin = getPlugin (pluginJarInfo .getJarName ());
189+ if (plugin == null ) {
190+ throw new IllegalStateException ("没有找到插件 " + pluginJarInfo .getJarName ());
191+ }
192+
193+ File file = pluginJarInfo .getFile ();
194+ this .currentProgressListener =
195+ (bytesRead , contentLength , done ) -> onProgressUpdate .accept (contentLength , bytesRead );
196+
197+ try (
198+ Response response = pluginDownloader
199+ .newCall (new Builder ().url (pluginJarInfo .getDownloadUrl ()).build ())
200+ .execute ();
201+ InputStream inputStream = response .body ().byteStream ();
202+ FileOutputStream outputStream = new FileOutputStream (file )
203+ ) {
204+ IOUtils .copy (inputStream , outputStream );
205+ }
206+
207+ plugin .setIsDownload (true );
208+ plugin .setIsEnable (true );
209+ plugin .setLocalVersionNumber (plugin .getVersionNumber ());
210+ return file ;
211+ }
212+
166213 ////////////////////////////////////////////////////////////// 保存配置
167214
168215 public void saveToFile () throws IOException {
169216 String json = JSON .toJSONString (this .pluginList , true );
170- Files .write (
171- Paths .get (this .localPluginsPath ),
172- json .getBytes (DEFAULT_CHARSET )
173- );
217+ Path path = Paths .get (this .localPluginsPath );
218+ if (!Files .exists (path )) {
219+ Files .createFile (path );
220+ }
221+ Files .write (path , json .getBytes (DEFAULT_CHARSET ));
174222 }
175223
176224 public void saveToFileQuietly () {
@@ -180,4 +228,77 @@ public void saveToFileQuietly() {
180228 log .error ("" , e );
181229 }
182230 }
231+
232+ ////////////////////////////////////////////////////////////// 下载进度
233+
234+ private ProgressListener currentProgressListener ;
235+
236+ private class DownloadProgressInterceptor implements Interceptor {
237+
238+ @ Override
239+ public Response intercept (Chain chain ) throws IOException {
240+ Response originalResponse = chain .proceed (chain .request ());
241+ return originalResponse .newBuilder ()
242+ .body (new ProgressResponseBody (originalResponse .body (),
243+ (bytesRead , contentLength , done ) -> {
244+ if (currentProgressListener != null ) {
245+ currentProgressListener .update (bytesRead , contentLength , done );
246+ }
247+ }
248+ ))
249+ .build ();
250+ }
251+ }
252+
253+ private static class ProgressResponseBody extends ResponseBody {
254+
255+ private final ResponseBody responseBody ;
256+
257+ private final ProgressListener progressListener ;
258+
259+ private BufferedSource bufferedSource ;
260+
261+ ProgressResponseBody (ResponseBody responseBody , ProgressListener progressListener ) {
262+ this .responseBody = responseBody ;
263+ this .progressListener = progressListener ;
264+ }
265+
266+ @ Override
267+ public MediaType contentType () {
268+ return responseBody .contentType ();
269+ }
270+
271+ @ Override
272+ public long contentLength () {
273+ return responseBody .contentLength ();
274+ }
275+
276+ @ Override
277+ public BufferedSource source () {
278+ if (bufferedSource == null ) {
279+ bufferedSource = Okio .buffer (source (responseBody .source ()));
280+ }
281+ return bufferedSource ;
282+ }
283+
284+ private Source source (Source source ) {
285+ return new ForwardingSource (source ) {
286+ long totalBytesRead = 0L ;
287+
288+ @ Override
289+ public long read (Buffer sink , long byteCount ) throws IOException {
290+ long bytesRead = super .read (sink , byteCount );
291+ // read() returns the number of bytes read, or -1 if this source is exhausted.
292+ totalBytesRead += bytesRead != -1 ? bytesRead : 0 ;
293+ progressListener .update (totalBytesRead , responseBody .contentLength (), bytesRead == -1 );
294+ return bytesRead ;
295+ }
296+ };
297+ }
298+ }
299+
300+ interface ProgressListener {
301+
302+ void update (long bytesRead , long contentLength , boolean done );
303+ }
183304}
0 commit comments