content: package com.sqmusicplus.v3.utils;
  
  import com.alibaba.fastjson.JSONObject;
  import com.sqmusicplus.v3.download.vo.DownloadProgress;
  import lombok.extern.slf4j.Slf4j;
  import okhttp3.*;
  import org.apache.hc.core5.util.TextUtils;
  
  import javax.net.ssl.*;
  import java.io.*;
  import java.security.KeyManagementException;
  import java.security.NoSuchAlgorithmException;
  import java.security.SecureRandom;
  import java.security.cert.CertificateException;
  import java.security.cert.X509Certificate;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.concurrent.TimeUnit;
  import java.util.function.Consumer;
  
  /**
   * @Classname DownloadUtils
   * @Description 下载工具类
   * @Version 1.0.0
   * @Date 2022/5/31 15:14
   * @Created by SQ
   */
  @Slf4j
  public class DownloadUtils {
  
      private static OkHttpClient okHttpClient;
  
  
      //下载其他的
     public static void download(String url, String path, String fileName, Consumer<DownloadProgress> onProcess, Consumer<File> onSuccess) {
  
         String s = "";
         if(fileName != null){
              s = path + fileName;
         }else{
             s = path;
         }
         File file = new File(s);
         download(url, file,null, onProcess, onSuccess, null,null);
      }
      public static void download(String url, File  file, Consumer<DownloadProgress> onProcess, Consumer<File> onSuccess,Consumer<Exception> onFailure,Consumer<File> onComplete) {
          download(url, file,null, onProcess, onSuccess, onFailure,onComplete);
      }
      public static void download(String url, String  file, Consumer<DownloadProgress> onProcess, Consumer<File> onSuccess,Consumer<Exception> onFailure,Consumer<File> onComplete) {
          File file1 = new File(file);
          download(url, file1,null, onProcess, onSuccess, onFailure,onComplete);
      }
  
      public static void download(String url ,File file,Consumer<File> onSuccess,Consumer<Exception> onFailure){
          download(url,file,null,null,onSuccess,onFailure,null);
      }
      public static void download(String url ,String path,Consumer<File> onSuccess,Consumer<Exception> onFailure){
          File file = new File(path);
          download(url,file,null,null,onSuccess,onFailure,null);
      }
      public static void download(String url ,File file,HashMap<String,String> headers,Consumer<File> onSuccess,Consumer<Exception> onFailure){
          download(url,file,headers,null,onSuccess,onFailure,null);
      }
      public static void download(String url ,File file,Consumer<File> onSuccess,Consumer<Exception> onFailure,Consumer<File> onComplete){
          download(url,file,null,null,onSuccess,onFailure,onComplete);
      }
      public static void download(String url , File file, HashMap<String,String> headers, Consumer<File> onSuccess, Consumer<Exception> onFailure, Consumer<File> onComplete){
          download(url,file,headers,null,onSuccess,onFailure,onComplete);
      }
  
  
      /**
       * @param url 下载连接
       * @param target 储存地址（无文件信息回自动创建）
       * @param headers url头文件信息
       * @param onProcess 进度回调
       * @param onSuccess 下载成功回调
       * @param onFailure 下载失败回调
       * @param onComplete 下载完成回调
       */
      public static void download( String url,
                            File target,
                            HashMap<String, String> headers,
                            Consumer<DownloadProgress> onProcess,
                            Consumer<File> onSuccess,
                            Consumer<Exception> onFailure,
                            Consumer<File> onComplete) {
                  Request.Builder builder = new Request.Builder()
                          .url(url)
                          .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
  
                  if (headers != null) {
                      headers.put("Accept", "application/xml;version=1");
                      headers.forEach(builder::addHeader);
                  } else {
                      builder.addHeader("Accept", "application/xml;version=1");
                  }
  
                  Request request = builder.build();
      if (okHttpClient== null){
          okHttpClient = getOkHttpClient();
      }
  
          okHttpClient.newCall(request).enqueue(new Callback() {
              @Override
              public void onFailure(Call call, IOException e) {
                  // 下载失败
                  onFailure.accept(e);
              }
              @Override
              public void onResponse(Call call, Response response) throws IOException {
  
                  ResponseBody body = response.body();
                  if (body == null) {
                      throw new IOException("Empty response body");
                  }
  
                  // 如果是文件夹，则生成目标文件
                  File file;
                  if (target.isDirectory()) {
                      String fileName = getHeaderFileName(response); // 从 URL 提取文件名
                      file = new File(target, fileName);
                      target.mkdirs();
                  } else {
                      file = target;
                      file.getParentFile().mkdirs();
                  }
  
  
                  InputStream is = null;
                  byte[] buf = new byte[2048];
                  int len = 0;
                  FileOutputStream fos = null;
  
                  try {
                      is = response.body().byteStream();
                      long total = response.body().contentLength();
                      fos = new FileOutputStream(file);
                      long sum = 0;
                      while ((len = is.read(buf)) != -1) {
                          fos.write(buf, 0, len);
                          sum += len;
                          int progress = (int) (sum * 1.0f / total * 100);
                          // 下载中
                          onProcess.accept(new DownloadProgress(sum, total, progress));
                      }
                      fos.flush();
                      // 下载完成
                      onSuccess.accept(file);
                  } catch (Exception e) {
                      onFailure.accept(e);
                  } finally {
                      try {
                          if (is != null)
                              is.close();
                      } catch (IOException e) {
                      }
                      try {
                          if (fos != null)
                              fos.close();
                      } catch (IOException e) {
                      }
                      onComplete.accept(file);
                  }
              }
          });
      }
  
  
  
  
  
  
  
  
  
  
  
  
  
      public static void download(String url, String path, Consumer<File> onSuccess) {
          download(url,path,null,null,onSuccess);
      }
  
  
      private static X509TrustManager myTrustManager = new X509TrustManager() {
  
          @Override
          public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
  
          @Override
          public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
  
          @Override
          public X509Certificate[] getAcceptedIssuers() {
              return new X509Certificate[0];
          }
      };
  
      private static HostnameVerifier myHostnameVerifier = new HostnameVerifier() {
          @Override
          public boolean verify(String hostname, SSLSession session) {
              return true;
          }
      };
  
  
  
      /**
       * @param url          下载连接
       * @param file  下载的文件
       */
  
      public static boolean download(final String url, final File file)  {
  
      if(okHttpClient==null){
          okHttpClient = getOkHttpClient();
      }
  
          Request request = new Request.Builder()
                  .url(url)
                  .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
                  .get()
                  .build();
  
          Response response = null;
          try {
              response = okHttpClient.newCall(request).execute();
          } catch (IOException e) {
          }
          InputStream inputStream = response.body().byteStream();
          return WriteFile4InputStream(file, inputStream);
  
      }
  
  
  
      public static boolean WriteFile4InputStream(File file, InputStream inputStream)
      {
          //默认为flase 即失败
          boolean result = false;
          try {
              if (!file.exists()){
                  file.getParentFile().mkdirs();
              }
              OutputStream os = new FileOutputStream(file);
              os.write(inputStream.readAllBytes());
              os.flush();
              // 确保文件完全写入磁盘
              ((FileOutputStream) os).getFD().sync();
              os.close();
              result = true;
          }catch (IOException e)
          {
              e.printStackTrace();
              result = false;
          }
          return result;
      }
  
      public static  OkHttpClient getOkHttpClient() {
         return getOkHttpClient(true);
      }
      public static  OkHttpClient getOkHttpClient(boolean followRedirects ) {
          if(okHttpClient==null){
              SSLContext sslCtx = null;
              try {
                  sslCtx = SSLContext.getInstance("TLS");
                  sslCtx.init(null, new TrustManager[] { myTrustManager }, new SecureRandom());
              } catch (NoSuchAlgorithmException e) {
                  throw new RuntimeException(e);
              } catch (KeyManagementException e) {
                  throw new RuntimeException(e);
              }
  //            builder.sslSocketFactory(mySSLSocketFactory, myTrustManager);
  //            builder.hostnameVerifier(myHostnameVerifier);
              SSLSocketFactory mySSLSocketFactory = sslCtx.getSocketFactory();
              okHttpClient = new OkHttpClient.Builder()
                      .connectTimeout(15, TimeUnit.SECONDS)
                      .writeTimeout(20, TimeUnit.SECONDS)
                      .readTimeout(20, TimeUnit.SECONDS)
                      .hostnameVerifier((hostName, session) -> true)
                      .protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
                      .retryOnConnectionFailure(true)
                      .followRedirects(followRedirects)
                      .followSslRedirects(followRedirects)
                      .sslSocketFactory(mySSLSocketFactory, myTrustManager)
                      .hostnameVerifier(myHostnameVerifier)
                      .build();
  
          }
          return okHttpClient;
      }
      public  static <T> T get(String url,HashMap<String,String> params,Class<T> clazz){
          OkHttpUtils builder = OkHttpUtils.builder().url(url);
  
          if (params!=null){
              builder.addParam(params);
          }
          String sync =builder
                  .get().sync();
          return (T)JSONObject.parseObject(sync,clazz);
  
      }
      public static JSONObject getToJsonObject(String url,HashMap<String,String> params){
          OkHttpUtils builder = OkHttpUtils.builder().url(url);
  
          if (params!=null){
              builder.addParam(params);
          }
          String sync =builder
                  .get().sync();
          return JSONObject.parseObject(sync);
  
      }
      public static <T> T get(String url ,Class<T> clazz){
          return get(url,null,clazz);
      }
  
      public static JSONObject getToJsonObject(String url){
          return getToJsonObject(url,null);
      }
  
  
      public static JSONObject postToJsonObject(String url,String body){
          OkHttpUtils builder = OkHttpUtils.builder().url(url);
          String sync =builder
                  .post(true,body)
                  .sync();
                  return JSONObject.parseObject(sync);
  
      }
  
  
      /**
       * 解析文件头
       * Content-Disposition:attachment;filename=FileName.txt
       * Content-Disposition: attachment; filename*="UTF-8''%E6%9B%BF%E6%8D%A2%E5%AE%9E%E9%AA%8C%E6%8A%A5%E5%91%8A.pdf"
       */
      private static String getHeaderFileName(Response response) {
          String dispositionHeader = response.header("Content-Disposition");
          if (!TextUtils.isEmpty(dispositionHeader)) {
              // 处理 filename*=utf-8''encoded_filename 格式
              if (dispositionHeader.contains("filename*=")) {
                  String[] parts = dispositionHeader.split("filename\\*=");
                  if (parts.length > 1) {
                      String encodedFilename = parts[1];
                      // 移除可能的引号
                      encodedFilename = encodedFilename.replace("\"", "");
                      // 处理 UTF-8'' 格式
                      if (encodedFilename.contains("UTF-8''")) {
                          String[] utf8Parts = encodedFilename.split("UTF-8''");
                          if (utf8Parts.length > 1) {
                              try {
                                  return java.net.URLDecoder.decode(utf8Parts[1], "UTF-8");
                              } catch (Exception e) {
                                  return utf8Parts[1];
                              }
                          }
                      }
                      return encodedFilename;
                  }
              }
              // 处理普通的 filename= 格式
              else if (dispositionHeader.contains("filename=")) {
                  String[] parts = dispositionHeader.split("filename=");
                  if (parts.length > 1) {
                      String filename = parts[1];
                      // 移除可能的引号
                      filename = filename.replace("\"", "");
                      return filename;
                  }
              }
              // 原有逻辑作为备选方案
              dispositionHeader = dispositionHeader.replace("attachment;filename=", "");
              dispositionHeader = dispositionHeader.replace("filename*=utf-8", "");
              String[] strings = dispositionHeader.split("; ");
              if (strings.length > 1) {
                  dispositionHeader = strings[1].replace("filename=", "");
                  dispositionHeader = dispositionHeader.replace("\"", "");
                  return dispositionHeader;
              }
              return "";
          }
          
          // 如果没有Content-Disposition头，则根据Content-Type生成随机文件名
          String contentType = response.header("Content-Type");
          return generateRandomFileName(contentType);
      }
  
      /**
       * 根据Content-Type生成随机文件名
       * @param contentType Content-Type头
       * @return 随机文件名
       */
      private static String generateRandomFileName(String contentType) {
          String extension = getFileExtensionFromContentType(contentType);
          String fileName = "file_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 10000);
          if (!extension.isEmpty()) {
              fileName += "." + extension;
          }
          return fileName;
      }
  
      /**
       * 根据Content-Type获取文件扩展名
       * @param contentType Content-Type头
       * @return 文件扩展名
       */
      private static String getFileExtensionFromContentType(String contentType) {
          if (TextUtils.isEmpty(contentType)) {
              return "";
          }
          
          switch (contentType.toLowerCase()) {
              case "image/jpeg":
                  return "jpg";
              case "image/png":
                  return "png";
              case "image/gif":
                  return "gif";
              case "image/webp":
                  return "webp";
              case "audio/mpeg":
                  return "mp3";
              case "audio/flac":
                  return "flac";
              case "audio/wav":
                  return "wav";
              case "audio/mp4":
                  return "m4a";
              case "video/mp4":
                  return "mp4";
              case "text/plain":
                  return "txt";
              case "text/html":
                  return "html";
              case "application/json":
                  return "json";
              case "application/pdf":
                  return "pdf";
              case "application/zip":
                  return "zip";
              default:
                  // 尝试从content-type中提取扩展名
                  if (contentType.contains("/")) {
                      String[] parts = contentType.split("/");
                      return parts[1].split(";")[0]; // 处理如 "text/html; charset=utf-8" 的情况
                  }
                  return "";
          }
      }
  
      /**
       * 获取body的字符串不转化
       * @param url
       * @param params
       * @return
       */
      public  static String getBodyStr(String url,HashMap<String,String> params){
          OkHttpUtils builder = OkHttpUtils.builder().url(url);
  
          if (params!=null){
              builder.addParam(params);
          }
          return  builder
                  .get().sync();
      }
      public  static String getBodyStr(String url){
          OkHttpUtils builder = OkHttpUtils.builder().url(url);
          return getBodyStr(url,null);
      }
  
  }