多线程断点下载

转载自
  1. import java.net.HttpURLConnection;  
  2. import java.net.URL;  
  3.   
  4.   
  5. public class Main {  
  6.       
  7.     private static Messages messages = new Messages();  
  8.       
  9.     private static CallBack callback = new CallBack() {  
  10.         @Override  
  11.         public void schedule(Messages messages) {  
  12.             long totalDownloadSize = messages.getTotalDownloadSize();  
  13.             int currentDownloadSize = messages.getCurrentDownloadSize();  
  14.             System.out.println((float)currentDownloadSize * 100.0f / (float)totalDownloadSize + "%");  
  15.         }  
  16.           
  17.         @Override  
  18.         public void handlerMessages(Messages messages) {  
  19.               
  20.         }  
  21.           
  22.         @Override  
  23.         public void fail(Messages messages) {  
  24.             System.out.println(messages.getMsg());  
  25.         }  
  26.     };  
  27.       
  28.     public static void main(String[] args) throws Exception {  
  29.         long startTime = System.currentTimeMillis();  
  30.         String downloadUrl = "http://www.xiazaiba.com/route.php?ct=stat&ac=stat_ads&id=CG8BOVNg&g=aHR0cDovL3d3dy51aWJpYS5jb20vamttc2V0dXAuZXhl";  
  31.         URL url = new URL(downloadUrl);  
  32.         HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  33.         conn.setRequestMethod("GET");  
  34.         conn.setReadTimeout(1000 * 5);  
  35.         conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");  
  36.         conn.setRequestProperty("Accept-Language", "zh-CN");  
  37.         conn.setRequestProperty("Referer", downloadUrl);   
  38.         conn.setRequestProperty("Charset", "UTF-8");  
  39.         conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");  
  40.         conn.setRequestProperty("Connection", "Keep-Alive");  
  41.         conn.connect();  
  42.           
  43.         if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {  
  44.             FileDownloader loader = new FileDownloader(callback, 4, messages);  
  45.             int size = conn.getContentLength();//根据响应获取文件大小  
  46.             loader.setSavePath("E:\\haoya.exe");  
  47.             loader.setConfigPath("E:\\haoya.properties");  
  48.             loader.download(downloadUrl, size);  
  49.         }else{  
  50.             System.out.println(conn.getResponseCode());  
  51.         }  
  52.           
  53.         conn.disconnect();  
  54.         long endTime = System.currentTimeMillis();  
  55.         System.out.println("Time" + (endTime - startTime));  
  56.     }  
  57. }  

FileDownloader.java

[html]  view plain copy
  1. import java.io.File;  
  2. import java.io.IOException;  
  3. import java.io.RandomAccessFile;  
  4. import java.net.URL;  
  5. import java.util.Map;  
  6.   
  7. public class FileDownloader implements DownLoadCallBack {  
  8.     //单个线程已经下载的长度  
  9.     private int downloadSize;  
  10.     //线程数   
  11.     private DownloadThread[] threads;  
  12.     // 每条线程下载的长度   
  13.     private int block;  
  14.     //文件保存路径  
  15.     private String savePath;  
  16.     //配置文件路径  
  17.     private String configPath;  
  18.     private File saveFile;  
  19.     private boolean isNext;  
  20.     private int call_curr;  
  21.     private int call_total;  
  22.     private PropertiesHelper propertiesHelper;      
  23.     private Map<String, String> datas;  
  24.     private Messages messages;  
  25.     private CallBack callback;  
  26.     //总的下载长度  
  27.     private int downloadLength;  
  28.     //是否退出下载  
  29.     private boolean exit;  
  30.   
  31.     public FileDownloader(CallBack callback, int threadNum, Messages messages) {  
  32.         this.callback = callback;  
  33.         this.threads = new DownloadThread[threadNum];  
  34.         this.isNext = false;  
  35.         this.call_total = threadNum;  
  36.         this.downloadSize = 0;  
  37.         this.downloadLength = 0;  
  38.         this.messages = messages;  
  39.         this.exit = false;  
  40.     }  
  41.   
  42.     /**  
  43.      * 开始下载文件  
  44.      */  
  45.     public void download(String downloadUrl, long size){  
  46.         try {  
  47.             messages.setTotalDownloadSize(size);  
  48.             saveFile = new File(savePath);  
  49.             RandomAccessFile accessFile = new RandomAccessFile(saveFile, "rw");  
  50.             accessFile.setLength(size);   
  51.             accessFile.close();  
  52.               
  53.             block = (int) ((size % this.threads.length) == 0 ? size  
  54.                     / this.threads.length : size / this.threads.length + 1);  
  55.             URL url = new URL(downloadUrl);  
  56.   
  57.             propertiesHelper = new PropertiesHelper(configPath);   
  58.             //如果配置文件不存在,则创建  
  59.             if(!FileOperate.isFileExist(configPath)){  
  60.                 FileOperate.createFile(configPath, false);  
  61.                 initProperties();  
  62.             }else{        
  63.                 datas = propertiesHelper.getAllProperties();  
  64.                 if(datas.size() == 0) initProperties();  
  65.             }     
  66.   
  67.             for(int i = 0; i < threads.length; i++){  
  68.                 String value = datas.get(Constant.THREADID + (i + 1));  
  69.                 if(value == null) value = "0";  
  70.                 downloadSize = Integer.parseInt(value);    
  71.                 downloadLength += downloadSize;  
  72.             }  
  73.   
  74.             for (int i = 0; i < threads.length; i++) {// 开启线程进行下载   
  75.                 String value = datas.get(String.valueOf(Constant.THREADID + (i + 1)));  
  76.                 if(value == null) value = "0";  
  77.                 downloadSize = Integer.parseInt(value);    
  78.                 if(downloadSize == block){    
  79.                     call_curr++;  
  80.                     if(call_curr == call_total){  
  81.                         isNext = true;  
  82.                         break;  
  83.                     }  
  84.                     continue;  
  85.                 }    
  86.                 threads[i] = new DownloadThread(url, saveFile, block,    
  87.                         downloadSize, i + 1, messages, callback, this);  
  88.                 threads[i].setPriority(7);  
  89.                 threads[i].start();  
  90.             }  
  91.                
  92.             while(!isNext());  
  93.         } catch (IOException e) {   
  94.             messages.setMsg("FileDownloader:download:" + e.getMessage());  
  95.             callback.fail(messages);    
  96.         }  
  97.     }     
  98.   
  99.     private synchronized boolean isNext() {  
  100.         return isNext;  
  101.     }  
  102.       
  103.     private void initProperties() {  
  104.         for(int i = 0; i < threads.length; i++){  
  105.             propertiesHelper.setProperties(Constant.THREADID + (i + 1), "0");   
  106.         }  
  107.         datas = propertiesHelper.getAllProperties();  
  108.     }  
  109.   
  110.     @Override  
  111.     public synchronized void isFinished() {  
  112.         call_curr++;  
  113.         if (call_curr == call_total){  
  114.             isNext = true;  
  115.         }  
  116.     }  
  117.   
  118.     public synchronized void update(String key, String value){  
  119.         propertiesHelper.setProperties(key, value);  
  120.     }  
  121.   
  122.     @Override    
  123.     public synchronized int getDownloadLength() {  
  124.         return downloadLength;  
  125.     }  
  126.   
  127.     @Override  
  128.     public synchronized void append(int size) {  
  129.         downloadLength += size;  
  130.     }  
  131.   
  132.     public String getSavePath() {  
  133.         return savePath;  
  134.     }  
  135.   
  136.     public void setSavePath(String savePath) {  
  137.         this.savePath = savePath;  
  138.     }  
  139.   
  140.     public String getConfigPath() {  
  141.         return configPath;  
  142.     }  
  143.   
  144.     public void setConfigPath(String configPath) {  
  145.         this.configPath = configPath;  
  146.     }  
  147.   
  148.     @Override  
  149.     public boolean getExit() {  
  150.         return exit;  
  151.     }  
  152.   
  153.     @Override  
  154.     public void setExit(boolean isExit) {  
  155.         exit = isExit;  
  156.     }  
  157. }  


DownloadThread.java

[html]  view plain copy
  1. import java.io.File;  
  2. import java.io.IOException;  
  3. import java.io.InputStream;  
  4. import java.io.RandomAccessFile;  
  5. import java.net.HttpURLConnection;  
  6. import java.net.URL;  
  7.   
  8. public class DownloadThread extends Thread {  
  9.     private File saveFile;  
  10.     private URL downUrl;  
  11.     private int block;  
  12.     private int threadId = -1;      
  13.     private CallBack callback;  
  14.     private DownLoadCallBack downLoadCallBack;  
  15.     private int downloadSize;  
  16.     private Messages messages;   
  17.   
  18.     public DownloadThread(URL downUrl, File saveFile, int block, int downloadSize, int threadId, Messages messages, CallBack callback, DownLoadCallBack downLoadCallBack) {  
  19.         this.downLoadCallBack = downLoadCallBack;  
  20.         this.downUrl = downUrl;  
  21.         this.saveFile = saveFile;  
  22.         this.block = block;  
  23.         this.threadId = threadId;  
  24.         this.callback = callback;  
  25.         this.downloadSize = downloadSize;  
  26.         this.messages = messages;  
  27.     }  
  28.   
  29.     @Override  
  30.     public void run() {  
  31.         HttpURLConnection conn = null;  
  32.         InputStream inStream = null;  
  33.         RandomAccessFile threadfile = null;     
  34.         try {    
  35.             conn = (HttpURLConnection) downUrl.openConnection();  
  36.             conn.setRequestMethod(Constant.GET);  
  37.             conn.setReadTimeout(1000 * 8);  
  38.             int startPos = block * (threadId - 1) + downloadSize;//开始位置     
  39.             int endPos = block * threadId -1;//结束位置    
  40.             conn.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);//设置获取实体数据的范围  
  41.   
  42.             if(conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL){  
  43.                 inStream = conn.getInputStream();     
  44.                 byte[] buffer = new byte[Constant.BUFFER * 10];  
  45.                 int offset = 0;  
  46.                 threadfile = new RandomAccessFile(saveFile, "rwd");    
  47.                 threadfile.seek(startPos);  
  48.                 String key = Constant.THREADID + String.valueOf(threadId);    
  49.                 //messages,文件总大小,下载的当前大小  
  50.                 while (!downLoadCallBack.getExit() && (offset = inStream.read(buffer)) != -1) {     
  51.                     threadfile.write(buffer, 0, offset);     
  52.                     downLoadCallBack.append(offset);        
  53.                     downloadSize += offset;  
  54.                     downLoadCallBack.update(key, String.valueOf(downloadSize));  
  55.                     int size = downLoadCallBack.getDownloadLength();  
  56.                     messages.setCurrentDownloadSize(size);  
  57.                     callback.schedule(messages);     
  58.                 }  
  59.             }  
  60.         } catch (IOException e) {  
  61.             downLoadCallBack.setExit(true);  
  62.             messages.setMsg("第" + threadId + "条线程下载失败");   
  63.             callback.fail(messages);     
  64.         }finally{   
  65.             try {  
  66.                 if(threadfile != null){  
  67.                     threadfile.close();    
  68.                 }  
  69.                 if(inStream != null){    
  70.                     inStream.close();  
  71.                 }   
  72.                 if(conn != null){  
  73.                     conn.disconnect();  
  74.                 }  
  75.             } catch (IOException e) {  
  76.                 e.printStackTrace();  
  77.             }  
  78.             downLoadCallBack.isFinished();  
  79.         }  
  80.     }  
  81. }  


DownLoadCallBack.java

[html]  view plain copy
  1. public interface DownLoadCallBack {  
  2.     public void isFinished();  
  3.       
  4.     public void update(String key, String value);  
  5.       
  6.     public void append(int size);  
  7.       
  8.     public int getDownloadLength();  
  9.       
  10.     public boolean getExit();  
  11.       
  12.     public void setExit(boolean isExit);  
  13. }  

CallBack.java

[html]  view plain copy
  1. public interface CallBack {  
  2.       
  3.     public void handlerMessages(Messages messages);  
  4.       
  5.     public void schedule(Messages messages);  
  6.       
  7.     public void fail(Messages messages);  
  8. }  

FileOperate.java

[html]  view plain copy
  1. import java.io.BufferedInputStream;  
  2. import java.io.BufferedOutputStream;  
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.FileNotFoundException;  
  6. import java.io.FileOutputStream;  
  7. import java.io.IOException;  
  8. import java.util.ArrayList;  
  9. import java.util.LinkedList;  
  10. import java.util.List;  
  11.   
  12. public class FileOperate {  
  13.     /**  
  14.      * 在指定路径下创建新文件  
  15.      * @param filePath      文件路径  
  16.      * @return  
  17.      * @throws IOException  
  18.      */  
  19.     public static File createFile(String filePath, boolean isDeleteAllFiles) {  
  20.         String parentPath = filePath.substring(0, filePath.lastIndexOf(File.separator));  
  21.         createFolders(parentPath, isDeleteAllFiles);  
  22.         File file = new File(filePath);  
  23.         try {  
  24.             if(!file.createNewFile()){  
  25.                 file.delete();  
  26.                 file.createNewFile();  
  27.             }  
  28.         } catch (IOException e) {  
  29.             e.printStackTrace();  
  30.         }  
  31.         return file;  
  32.     }  
  33.   
  34.     /**  
  35.      * 判断配置文件是否存在  
  36.      * @param filePath  文件路径  
  37.      * @return  
  38.      */  
  39.     public static boolean isFileExist(String filePath){  
  40.         File file = new File(filePath);  
  41.         return file.exists();  
  42.     }  
  43.   
  44.     /**  
  45.      * 取得指定目录下的所有文件夹名  
  46.      * @return  
  47.      */  
  48.     public static List<String> getFilesName(String filePath){  
  49.         List<String> files_name = null;  
  50.         File file = new File(filePath);  
  51.         if(file.exists()){  
  52.             files_name = new ArrayList<String>();  
  53.             File[] files = file.listFiles();  
  54.             for (File file2 : files) {  
  55.                 if(file2.isDirectory()){  
  56.                     files_name.add(file2.getName());  
  57.                 }  
  58.             }  
  59.         }  
  60.         return files_name;  
  61.     }  
  62.   
  63.     public static void deleteFiles(File file){  
  64.         if(file.exists()){  
  65.             File[] files = file.listFiles();  
  66.             for(File f : files){  
  67.                 if(f.isFile()) f.delete();  
  68.             }  
  69.         }  
  70.     }  
  71.   
  72.     public static File createFolders(String path, boolean isDeleteAllFiles){  
  73.         File file = new File(path);  
  74.         if(file.exists() && file.isDirectory()){  
  75.             if(isDeleteAllFiles) deleteFiles(file);  
  76.         }else{  
  77.             file.mkdirs();  
  78.         }  
  79.         return file;  
  80.     }  
  81.   
  82.     public static boolean deleteFile(String filePath){  
  83.         boolean isDeleted = false;  
  84.         File file = new File(filePath);  
  85.         if(file.exists()){  
  86.             if(file.isFile()) {  
  87.                 file.delete();  
  88.                 isDeleted = true;  
  89.             }  
  90.         }  
  91.         return isDeleted;  
  92.     }  
  93.   
  94.     public static void deleteMkdir(String filePath){  
  95.         File file = new File(filePath);  
  96.         if(file.exists() && file.isDirectory()){  
  97.             file.delete();  
  98.         }  
  99.     }  
  100.   
  101.   
  102.     /*********删除文件夹******/  
  103.     public static boolean deleteFolders(String filePath){  
  104.         boolean isDeleteSuccess = false;  
  105.         LinkedList<String> folderList = new LinkedList<String>();  
  106.         folderList.add(filePath);  
  107.   
  108.         while(folderList.size() > 0){  
  109.             File file1 = new File(folderList.poll());  
  110.             File[] files1 = file1.listFiles();  
  111.             ArrayList<File> fileList = new ArrayList<File>();  
  112.             for(int i = 0; i < fileList.size(); i++){  
  113.                 if(files1[i].isDirectory()){  
  114.                     folderList.add(files1[i].getPath());  
  115.                 }else{  
  116.                     fileList.add(files1[i]);  
  117.                 }  
  118.             }  
  119.             //删除文件  
  120.             for(File file : fileList){  
  121.                 file.delete();  
  122.             }  
  123.         }  
  124.   
  125.         //删除文件夹  
  126.         folderList = new LinkedList<String>();  
  127.         folderList.add(filePath);  
  128.         while(folderList.size() > 0){  
  129.             File file2 = new File(folderList.getLast());  
  130.             if(file2.delete()){  
  131.                 folderList.removeLast();  
  132.             }else{  
  133.                 File[] files2 = file2.listFiles();  
  134.                 for(int i = 0; i < files2.length; i++){  
  135.                     folderList.add(files2[i].getPath());  
  136.                 }  
  137.             }  
  138.         }  
  139.         if(folderList.size() == 0) isDeleteSuccess = true;  
  140.         return isDeleteSuccess;  
  141.     }  
  142.   
  143.     public static boolean moveFile(String srcFilePath, String dstFilePath){  
  144.         boolean isMoveFileSuccess = false;  
  145.           
  146.         BufferedInputStream bis = null;  
  147.         BufferedOutputStream bos = null;  
  148.         FileInputStream fis = null;  
  149.         FileOutputStream fos = null;  
  150.         try {  
  151.             if(isFileExist(dstFilePath)) deleteFile(dstFilePath);  
  152.             File dstFile = createFile(dstFilePath, false);  
  153.             fis = new FileInputStream(new File(srcFilePath));  
  154.             fos = new FileOutputStream(dstFile);  
  155.             bis = new BufferedInputStream(fis);  
  156.             bos = new BufferedOutputStream(fos, Constant.BUFFER * 10);  
  157.             int count = -1;  
  158.             byte[] buffer = new byte[Constant.BUFFER * 10];  
  159.             while((count = bis.read(buffer, 0, Constant.BUFFER * 10)) != -1){  
  160.                 bos.write(buffer, 0, count);  
  161.                 bos.flush();  
  162.             }  
  163.             isMoveFileSuccess = true;  
  164.         } catch (FileNotFoundException e) {  
  165.             e.printStackTrace();  
  166.         } catch (IOException e) {  
  167.             e.printStackTrace();  
  168.         }finally{  
  169.             try {  
  170.                 if(bos != null){  
  171.                     bos.close();  
  172.                 }  
  173.                 if(fos != null){  
  174.                     fos.close();  
  175.                 }  
  176.                 if(bis != null){  
  177.                     bis.close();  
  178.                 }  
  179.                 if(fis != null){  
  180.                     fis.close();  
  181.                 }  
  182.             } catch (IOException e) {  
  183.                 e.printStackTrace();  
  184.             }  
  185.         }  
  186.         return isMoveFileSuccess;  
  187.     }  
  188. }  


Constant.java

[html]  view plain copy
  1. public class Constant {  
  2.     public static final int BUFFER = 1024;  
  3.     public static final String UTF = "utf-8";  
  4.     public static final String THREADID = "threadId";    
  5.       
  6.     public static final String OGG = ".ogg";  
  7.     public static final String PACK = ".pack";  
  8.     public static final String PNG = ".png";  
  9.     public static final String MP3 = ".mp3";  
  10.     public static final String PROPERTIES = ".properties";  
  11.     public static final String TMX = ".tmx";  
  12.     public static final String GET = "GET";  
  13. }  

Messages.java

[html]  view plain copy
  1. public class Messages {  
  2.     private String msg;  
  3.     private long totalDownloadSize;  
  4.     private int currentDownloadSize;  
  5.     public String getMsg() {  
  6.         return msg;  
  7.     }  
  8.     public void setMsg(String msg) {  
  9.         this.msg = msg;  
  10.     }  
  11.     public long getTotalDownloadSize() {  
  12.         return totalDownloadSize;  
  13.     }  
  14.     public void setTotalDownloadSize(long totalDownloadSize) {  
  15.         this.totalDownloadSize = totalDownloadSize;  
  16.     }  
  17.     public int getCurrentDownloadSize() {  
  18.         return currentDownloadSize;  
  19.     }  
  20.     public void setCurrentDownloadSize(int currentDownloadSize) {  
  21.         this.currentDownloadSize = currentDownloadSize;  
  22.     }  
  23. }  

PropertiesHelper.java

[html]  view plain copy
  1. import java.io.FileInputStream;  
  2. import java.io.FileOutputStream;  
  3. import java.io.InputStream;  
  4. import java.util.Enumeration;  
  5. import java.util.HashMap;  
  6. import java.util.Map;  
  7. import java.util.Properties;  
  8.   
  9. public class PropertiesHelper {  
  10.   
  11.     private String project_root = "";  
  12.   
  13.     public PropertiesHelper(String filePath) {  
  14.         if (filePath != null && filePath.length() > 0) {  
  15.             project_root = project_root + filePath;  
  16.         }  
  17.     }  
  18.   
  19.     public String getProperties(String key) {  
  20.         InputStream fis = null;  
  21.         try {  
  22.             Properties prop = new Properties();  
  23.             fis = new FileInputStream(project_root);  
  24.             prop.load(fis);  
  25.             return prop.getProperty(key);  
  26.         } catch (Exception e) {  
  27.             e.printStackTrace();  
  28.         } finally {  
  29.             try {  
  30.                 if (fis != null)  
  31.                     fis.close();  
  32.             } catch (Exception e) {  
  33.                 e.printStackTrace();  
  34.             }  
  35.         }  
  36.         return null;  
  37.     }  
  38.   
  39.     public Map<String, String> getAllProperties() {  
  40.         InputStream fis = null;  
  41.         Map<String, String> params = new HashMap<String, String>();  
  42.         try {  
  43.             Properties prop = new Properties();  
  44.             fis = new FileInputStream(project_root);  
  45.             prop.load(fis);  
  46.             Enumeration en = prop.propertyNames();  
  47.             while (en.hasMoreElements()) {  
  48.                 String key = (String) en.nextElement();  
  49.                 String value = prop.getProperty(key);  
  50.                 params.put(key, value);  
  51.             }  
  52.         } catch (Exception e) {  
  53.             e.printStackTrace();  
  54.         } finally {  
  55.             try {  
  56.                 if (fis != null)  
  57.                     fis.close();  
  58.             } catch (Exception e) {  
  59.                 e.printStackTrace();  
  60.             }  
  61.         }  
  62.         return params;  
  63.     }  
  64.   
  65.     public void setProperties(String key, String value) {  
  66.         Properties prop = new Properties();  
  67.         FileOutputStream outputFile = null;  
  68.         InputStream fis = null;  
  69.         try {  
  70.             // 输入流和输出流要分开处理, 放一起会造成写入时覆盖以前的属性  
  71.             fis = new FileInputStream(project_root);  
  72.             // 先载入已经有的属性文件  
  73.             prop.load(fis);  
  74.   
  75.             // 追加新的属性  
  76.             prop.setProperty(key, value);  
  77.   
  78.             // 写入属性  
  79.             outputFile = new FileOutputStream(project_root);  
  80.             prop.store(outputFile, "");  
  81.   
  82.             outputFile.flush();  
  83.         } catch (Exception e) {  
  84.             e.printStackTrace();  
  85.         } finally {  
  86.             try {  
  87.                 if (fis != null)  
  88.                     fis.close();  
  89.                 if (outputFile != null)  
  90.                     outputFile.close();  
  91.             } catch (Exception e) {  
  92.                 e.printStackTrace();  
  93.             }  
  94.         }  
  95.     }  
  96. }  
基于Spring Boot搭建的一个多功能在线学习系统的实现细节。系统分为管理员和用户两个主要模块。管理员负责视频、文件和文章资料的管理以及系统运营维护;用户则可以进行视频播放、资料下载、参与学习论坛并享受个性化学习服务。文中重点探讨了文件下载的安全性和性能优化(如使用Resource对象避免内存溢出),积分排行榜的高效实现(采用Redis Sorted Set结构),敏感词过滤机制(利用DFA算法构建内存过滤树)以及视频播放的浏览器兼容性解决方案(通过FFmpeg调整MOOV原子位置)。此外,还提到了权限管理方面自定义动态加载器的应用,提高了系统的灵活性和易用性。 适合人群:对Spring Boot有一定了解,希望深入理解其实际应用的技术人员,尤其是从事在线教育平台开发的相关从业者。 使用场景及目标:适用于需要快速搭建稳定高效的在线学习平台的企业或团队。目标在于提供一套完整的解决方案,涵盖从资源管理到用户体验优化等多个方面,帮助开发者更好地理解和掌握Spring Boot框架的实际运用技巧。 其他说明:文中不仅提供了具体的代码示例和技术思路,还分享了许多实践经验教训,对于提高项目质量有着重要的指导意义。同时强调了安全性、性能优化等方面的重要性,确保系统能够应对大规模用户的并发访问需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值