最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot整合FFmpeg進行視頻處理的詳細教學

 更新時間:2026年01月05日 14:13:53   作者:悟空碼字  
這篇文章主要為大家詳細介紹了SpringBoot如何整合FFmpeg進行視頻處理,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下

第一部分:認識 FFmpeg 

FFmpeg 是什么?想象一下,如果你有一個朋友,他能:

  • 把 MP4 變成 AVI,就像把咖啡變成奶茶
  • 裁剪視頻,比理發(fā)師剪頭發(fā)還精準
  • 提取音頻,比從披薩上分離芝士還干凈
  • 壓縮視頻,比你把行李箱塞滿時還高效

這個“萬能朋友”就是 FFmpeg!它是一個開源的聲音/影像處理工具,功能強大到能讓好萊塢特效師失業(yè)(開玩笑的)。

# FFmpeg 的基本心態(tài):
# "給我一個視頻,我能還你一個世界"
# 實際上它想說的是:"ffmpeg -i input.mp4 [一堆參數(shù)] output.mp4"

第二部分:整合步驟 —— 像組裝樂高一樣簡單

步驟1:先給項目來點“開胃菜”—— Maven依賴

<!-- pom.xml -->
<dependencies>
    <!-- SpringBoot 標準配置 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- 讓我們記錄FFmpeg的“精彩表演” -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>
    
    <!-- 視頻處理時的“后悔藥”——異常處理 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
</dependencies>

步驟2:配置FFmpeg —— 像教AI用筷子

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "ffmpeg")
@Data
public class FFmpegConfig {
    /**
     * FFmpeg可執(zhí)行文件路徑
     * Windows: "C:/ffmpeg/bin/ffmpeg.exe"
     * Linux/Mac: "/usr/bin/ffmpeg"
     */
    private String path;
    
    /**
     * 超時時間(秒)
     * 防止視頻處理變成“永恒等待”
     */
    private Long timeout = 3600L;
    
    /**
     * 線程數(shù)
     * 多線程就像多雙手,干活更快!
     */
    private Integer threads = 4;
}
# application.yml
ffmpeg:
  path: /usr/local/bin/ffmpeg  # 你的FFmpeg安裝路徑
  timeout: 3600                # 1小時,足夠看一集電視劇了
  threads: 4                   # 4個線程,四核處理器的最愛

步驟3:創(chuàng)建FFmpeg指揮官

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Component
public class FFmpegCommander {
    
    @Autowired
    private FFmpegConfig ffmpegConfig;
    
    /**
     * 執(zhí)行FFmpeg命令
     * @param commands 命令參數(shù)(像給廚師遞菜單)
     * @return 是否成功(廚子有沒有把菜做糊)
     */
    public boolean execute(List<String> commands) {
        List<String> fullCommand = new ArrayList<>();
        fullCommand.add(ffmpegConfig.getPath());
        fullCommand.addAll(commands);
        
        log.info("FFmpeg開始干活啦!命令:{}", String.join(" ", fullCommand));
        
        ProcessBuilder processBuilder = new ProcessBuilder(fullCommand);
        processBuilder.redirectErrorStream(true); // 錯誤輸出也給我看看
        
        try {
            Process process = processBuilder.start();
            
            // 讀取輸出,防止FFmpeg“自言自語”沒人聽
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    log.debug("FFmpeg悄悄說:{}", line);
                }
            }
            
            // 等待處理完成,別急著催
            int exitCode = process.waitFor();
            boolean success = exitCode == 0;
            
            if (success) {
                log.info("FFmpeg完美收工!");
            } else {
                log.error("FFmpeg罷工了!退出碼:{}", exitCode);
            }
            
            return success;
            
        } catch (Exception e) {
            log.error("FFmpeg崩潰了,原因:{}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 獲取FFmpeg版本(驗明正身)
     */
    public String getVersion() {
        try {
            Process process = new ProcessBuilder(ffmpegConfig.getPath(), "-version").start();
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()));
            return reader.readLine(); // 第一行就是版本信息
        } catch (Exception e) {
            return "FFmpeg可能去度假了:" + e.getMessage();
        }
    }
}

步驟4:創(chuàng)建視頻處理服務 —— 你的私人視頻管家

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

@Slf4j
@Service
public class VideoService {
    
    @Autowired
    private FFmpegCommander ffmpegCommander;
    
    // 臨時文件存放目錄(像快遞的臨時存放點)
    private final String TEMP_DIR = System.getProperty("java.io.tmpdir") + "/video-process/";
    
    public VideoService() {
        // 確保臨時目錄存在
        new File(TEMP_DIR).mkdirs();
    }
    
    /**
     * 轉(zhuǎn)換視頻格式(像把中文翻譯成英文)
     * @param inputFile 輸入文件
     * @param targetFormat 目標格式(mp4, avi, mov...)
     */
    public File convertFormat(MultipartFile inputFile, String targetFormat) throws IOException {
        log.info("開始格式轉(zhuǎn)換:{} → {}", 
                 getFileExtension(inputFile.getOriginalFilename()), 
                 targetFormat);
        
        // 1. 保存上傳的文件(像把食材先放到廚房)
        File input = saveTempFile(inputFile);
        
        // 2. 準備輸出文件(準備好盤子)
        String outputFileName = UUID.randomUUID() + "." + targetFormat;
        File output = new File(TEMP_DIR + outputFileName);
        
        // 3. 構建FFmpeg命令菜單
        List<String> commands = Arrays.asList(
            "-i", input.getAbsolutePath(),     // 輸入文件
            "-threads", "4",                   // 用4個線程
            "-preset", "fast",                 // 快速預設
            "-c:v", "libx264",                 // 視頻編碼
            "-c:a", "aac",                     // 音頻編碼
            "-y",                              // 覆蓋輸出文件(別問我是否確定)
            output.getAbsolutePath()           // 輸出文件
        );
        
        // 4. 讓FFmpeg大廚開始烹飪
        boolean success = ffmpegCommander.execute(commands);
        
        // 5. 清理臨時文件(洗盤子)
        input.delete();
        
        if (success && output.exists()) {
            log.info("格式轉(zhuǎn)換成功!文件大?。簕} MB", 
                     output.length() / (1024 * 1024));
            return output;
        } else {
            throw new RuntimeException("轉(zhuǎn)換失敗,F(xiàn)Fmpeg可能去做美甲了");
        }
    }
    
    /**
     * 提取視頻縮略圖(給視頻拍證件照)
     */
    public File extractThumbnail(MultipartFile videoFile, int second) throws IOException {
        log.info("正在給視頻拍第{}秒的證件照...", second);
        
        File input = saveTempFile(videoFile);
        String outputFileName = UUID.randomUUID() + ".jpg";
        File output = new File(TEMP_DIR + outputFileName);
        
        List<String> commands = Arrays.asList(
            "-i", input.getAbsolutePath(),
            "-ss", String.valueOf(second),    // 跳轉(zhuǎn)到指定秒數(shù)
            "-vframes", "1",                  // 只要1幀
            "-vf", "scale=320:-1",           // 縮放到寬度320,高度自動
            "-y",
            output.getAbsolutePath()
        );
        
        boolean success = ffmpegCommander.execute(commands);
        input.delete();
        
        if (success && output.exists()) {
            log.info("縮略圖生成成功!");
            return output;
        }
        throw new RuntimeException("拍照失敗,視頻可能害羞了");
    }
    
    /**
     * 壓縮視頻(給視頻減肥)
     */
    public File compressVideo(MultipartFile videoFile, int targetBitrate) throws IOException {
        log.info("開始給視頻減肥,目標比特率:{}k", targetBitrate);
        
        File input = saveTempFile(videoFile);
        long originalSize = input.length();
        
        String outputFileName = UUID.randomUUID() + "_compressed.mp4";
        File output = new File(TEMP_DIR + outputFileName);
        
        List<String> commands = Arrays.asList(
            "-i", input.getAbsolutePath(),
            "-threads", "4",
            "-b:v", targetBitrate + "k",      // 目標視頻比特率
            "-b:a", "128k",                   // 音頻比特率
            "-y",
            output.getAbsolutePath()
        );
        
        boolean success = ffmpegCommander.execute(commands);
        input.delete();
        
        if (success && output.exists()) {
            long compressedSize = output.length();
            double ratio = (1.0 - (double)compressedSize/originalSize) * 100;
            log.info("減肥成功!原大小:{}MB,現(xiàn)大?。簕}MB,瘦身:{:.1f}%",
                     originalSize/(1024*1024),
                     compressedSize/(1024*1024),
                     ratio);
            return output;
        }
        throw new RuntimeException("減肥失敗,視頻可能偷吃宵夜了");
    }
    
    /**
     * 合并視頻和音頻(像給電影配音)
     */
    public File mergeVideoAudio(MultipartFile videoFile, 
                                MultipartFile audioFile) throws IOException {
        log.info("開始給視頻配音...");
        
        File video = saveTempFile(videoFile);
        File audio = saveTempFile(audioFile);
        
        String outputFileName = UUID.randomUUID() + "_merged.mp4";
        File output = new File(TEMP_DIR + outputFileName);
        
        List<String> commands = Arrays.asList(
            "-i", video.getAbsolutePath(),
            "-i", audio.getAbsolutePath(),
            "-c:v", "copy",                   // 視頻流直接復制(不重新編碼)
            "-c:a", "aac",                    // 音頻重新編碼
            "-map", "0:v:0",                  // 取第一個文件的視頻
            "-map", "1:a:0",                  // 取第二個文件的音頻
            "-shortest",                      // 以最短的流為準
            "-y",
            output.getAbsolutePath()
        );
        
        boolean success = ffmpegCommander.execute(commands);
        video.delete();
        audio.delete();
        
        if (success && output.exists()) {
            log.info("配音成功!新視頻誕生了");
            return output;
        }
        throw new RuntimeException("合并失敗,可能視頻和音頻在鬧離婚");
    }
    
    private File saveTempFile(MultipartFile file) throws IOException {
        String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
        Path path = Paths.get(TEMP_DIR + fileName);
        Files.copy(file.getInputStream(), path);
        return path.toFile();
    }
    
    private String getFileExtension(String filename) {
        if (filename == null) return "unknown";
        int dotIndex = filename.lastIndexOf('.');
        return (dotIndex == -1) ? "" : filename.substring(dotIndex + 1);
    }
}

步驟5:創(chuàng)建控制器 —— 視頻處理的接待處

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;

@Slf4j
@RestController
@RequestMapping("/api/video")
public class VideoController {
    
    @Autowired
    private VideoService videoService;
    
    @Autowired
    private FFmpegCommander ffmpegCommander;
    
    @GetMapping("/version")
    public String getFFmpegVersion() {
        String version = ffmpegCommander.getVersion();
        return "{\"version\": \"" + version + "\"}";
    }
    
    @PostMapping("/convert")
    public ResponseEntity<Resource> convertFormat(
            @RequestParam("file") MultipartFile file,
            @RequestParam("format") String format,
            HttpServletResponse response) throws IOException {
        
        log.info("收到轉(zhuǎn)換請求:{} → {}", file.getOriginalFilename(), format);
        
        File converted = videoService.convertFormat(file, format);
        
        return buildFileResponse(converted, 
                "converted." + format, 
                MediaType.APPLICATION_OCTET_STREAM);
    }
    
    @PostMapping("/thumbnail")
    public ResponseEntity<Resource> extractThumbnail(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "second", defaultValue = "5") int second) throws IOException {
        
        File thumbnail = videoService.extractThumbnail(file, second);
        
        return buildFileResponse(thumbnail,
                "thumbnail.jpg",
                MediaType.IMAGE_JPEG);
    }
    
    @PostMapping("/compress")
    public ResponseEntity<Resource> compressVideo(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "bitrate", defaultValue = "1000") int bitrate) throws IOException {
        
        File compressed = videoService.compressVideo(file, bitrate);
        
        return buildFileResponse(compressed,
                "compressed.mp4",
                MediaType.APPLICATION_OCTET_STREAM);
    }
    
    @PostMapping("/merge")
    public ResponseEntity<Resource> mergeVideoAudio(
            @RequestParam("video") MultipartFile video,
            @RequestParam("audio") MultipartFile audio) throws IOException {
        
        File merged = videoService.mergeVideoAudio(video, audio);
        
        return buildFileResponse(merged,
                "merged.mp4",
                MediaType.APPLICATION_OCTET_STREAM);
    }
    
    private ResponseEntity<Resource> buildFileResponse(File file, 
                                                       String filename,
                                                       MediaType mediaType) {
        if (!file.exists()) {
            return ResponseEntity.notFound().build();
        }
        
        Resource resource = new FileSystemResource(file);
        
        // 文件下載完成后自動刪除(深藏功與名)
        file.deleteOnExit();
        
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, 
                        "attachment; filename=\"" + filename + "\"")
                .contentType(mediaType)
                .contentLength(file.length())
                .body(resource);
    }
}

步驟6:添加異常處理 —— 給程序買份保險

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, Object>> handleException(Exception e) {
        log.error("系統(tǒng)鬧情緒了:{}", e.getMessage(), e);
        
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("message", "服務器開小差了,可能是FFmpeg在偷懶");
        response.put("error", e.getMessage());
        response.put("timestamp", System.currentTimeMillis());
        
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(response);
    }
    
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public ResponseEntity<Map<String, Object>> handleMaxSizeException() {
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("message", "文件太大了,服務器拿不動了");
        response.put("suggestion", "請嘗試壓縮視頻或上傳小一點的文件");
        
        return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE)
                .body(response);
    }
    
    @ExceptionHandler(IOException.class)
    public ResponseEntity<Map<String, Object>> handleIOException(IOException e) {
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("message", "文件讀寫出了問題,可能是磁盤在鬧脾氣");
        response.put("error", e.getMessage());
        
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(response);
    }
}

第三部分:使用示例 —— 讓我們來實際操練一下

1. 啟動應用程序

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class VideoProcessingApplication {
    public static void main(String[] args) {
        SpringApplication.run(VideoProcessingApplication.class, args);
        System.out.println("視頻處理服務啟動成功!");
        System.out.println("FFmpeg整裝待發(fā),隨時準備處理你的視頻");
    }
}

2. 測試API

使用Postman或curl測試:

# 查看FFmpeg版本
curl http://localhost:8080/api/video/version

# 轉(zhuǎn)換視頻格式
curl -X POST -F "file=@input.avi" -F "format=mp4" \
     http://localhost:8080/api/video/convert --output output.mp4

# 提取縮略圖
curl -X POST -F "file=@video.mp4" -F "second=10" \
     http://localhost:8080/api/video/thumbnail --output thumbnail.jpg

# 壓縮視頻
curl -X POST -F "file=@large_video.mp4" -F "bitrate=500" \
     http://localhost:8080/api/video/compress --output compressed.mp4

第四部分:高級技巧 —— 讓FFmpeg更懂你

1. 添加進度監(jiān)聽(給視頻處理加個進度條)

public interface ProgressListener {
    void onProgress(double percentage, String message);
    void onComplete(File outputFile);
    void onError(String error);
}

// 在FFmpegCommander中添加進度解析
private void parseProgress(String line, ProgressListener listener) {
    // 解析FFmpeg的輸出,提取進度信息
    // 示例輸出:frame=  123 fps=25.1 time=00:00:04.92 bitrate= 512.0kbits/s
    if (line.contains("time=")) {
        // 這里可以解析時間,計算進度百分比
        // 實際實現(xiàn)需要根據(jù)視頻總時長計算
    }
}

2. 批量處理(一次處理多個文件)

public List<File> batchConvert(List<MultipartFile> files, String format) {
    return files.parallelStream()  // 并行處理,更快!
            .map(file -> {
                try {
                    return videoService.convertFormat(file, format);
                } catch (IOException e) {
                    log.error("轉(zhuǎn)換失?。簕}", file.getOriginalFilename(), e);
                    return null;
                }
            })
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
}

3. 視頻信息提取(給視頻做體檢)

public Map<String, Object> getVideoInfo(File videoFile) {
    // 使用FFprobe(FFmpeg的小伙伴)獲取視頻信息
    List<String> commands = Arrays.asList(
        "-v", "error",
        "-select_streams", "v:0",
        "-show_entries", "stream=width,height,duration,bit_rate,codec_name",
        "-of", "json",
        videoFile.getAbsolutePath()
    );
    
    // 執(zhí)行命令并解析JSON結果
    // 返回包含分辨率、時長、碼率、編碼格式等信息
}

第五部分:總結與注意事項

成功整合的秘訣:

正確安裝FFmpeg:確保系統(tǒng)PATH中有FFmpeg,或者配置正確的路徑

# 檢查安裝
ffmpeg -version

資源管理

java -Xmx2g -jar your-application.jar
  • 視頻處理很吃內(nèi)存,記得給JVM足夠的內(nèi)存
  • 及時清理臨時文件,防止磁盤被撐爆

錯誤處理

  • FFmpeg可能會因為各種原因失敗(不支持的格式、損壞的文件等)
  • 添加重試機制和詳細的日志記錄

安全性

  • 限制上傳文件類型和大小
  • 對用戶輸入進行嚴格驗證
  • 防止命令注入攻擊

可能遇到的坑

  1. 跨平臺問題:Windows和Linux下的路徑差異
  2. 編碼問題:中文字符在命令中可能需要特殊處理
  3. 權限問題:確保應用有執(zhí)行FFmpeg的權限
  4. 性能問題:大文件處理可能需要很長時間,考慮異步處理

為什么選擇這個方案?

  1. 靈活性強:可以執(zhí)行任何FFmpeg支持的操作
  2. 功能全面:視頻處理界的"瑞士軍刀"
  3. 社區(qū)支持好:遇到問題容易找到解決方案
  4. 免費開源:省錢又省心

最后

FFmpeg就像一把強大的電鋸——功能強大但需要小心使用。不要在生產(chǎn)環(huán)境直接運行未經(jīng)驗證的命令,否則可能會:

  • 把服務器CPU燒得像烤紅薯
  • 讓磁盤空間消失得比錢包里的錢還快
  • 產(chǎn)生一堆讓你懷疑人生的臨時文件

但只要你按照本文的步驟,像對待一只溫順的貓一樣對待FFmpeg,它就會成為你在視頻處理領域最得力的助手!

以上就是SpringBoot整合FFmpeg進行視頻處理的詳細教學的詳細內(nèi)容,更多關于SpringBoot FFmpeg視頻處理的資料請關注腳本之家其它相關文章!

相關文章

  • JavaWeb?Servlet實現(xiàn)文件上傳與下載功能實例

    JavaWeb?Servlet實現(xiàn)文件上傳與下載功能實例

    因自己負責的項目中需要實現(xiàn)文件上傳,所以下面下面這篇文章主要給大家介紹了關于JavaWeb?Servlet實現(xiàn)文件上傳與下載功能的相關資料,需要的朋友可以參考下
    2022-04-04
  • Tomcat處理請求的線程模型詳解

    Tomcat處理請求的線程模型詳解

    這篇文章主要為大家詳細介紹了Tomcat處理請求的線程模型,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C/C++中的struct結構體詳細解讀

    C/C++中的struct結構體詳細解讀

    這篇文章主要介紹了C/C++中的struct結構體詳細解讀,結構體是由一批數(shù)據(jù)組合而成的結構型數(shù)據(jù),組成結構型數(shù)據(jù)的每個數(shù)據(jù)稱為結構型數(shù)據(jù)的“成員”,其描述了一塊內(nèi)存區(qū)間的大小及意義,需要的朋友可以參考下
    2023-10-10
  • Spring系列中的beanFactory與ApplicationContext

    Spring系列中的beanFactory與ApplicationContext

    這篇文章主要介紹了Spring系列中的beanFactory與ApplicationContext,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09
  • JPA使用樂觀鎖應對高并發(fā)方式

    JPA使用樂觀鎖應對高并發(fā)方式

    這篇文章主要介紹了JPA使用樂觀鎖應對高并發(fā)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • JVM虛擬機性能監(jiān)控與故障處理工具介紹

    JVM虛擬機性能監(jiān)控與故障處理工具介紹

    這篇文章主要為大家介紹了JVM虛擬機性能監(jiān)控與故障處理工具介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • HDFS的Java API的訪問方式實例代碼

    HDFS的Java API的訪問方式實例代碼

    這篇文章主要介紹了HDFS的Java API的訪問方式實例代碼,分享了相關代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-02-02
  • Java 實戰(zhàn)項目錘煉之醫(yī)院門診收費管理系統(tǒng)的實現(xiàn)流程

    Java 實戰(zhàn)項目錘煉之醫(yī)院門診收費管理系統(tǒng)的實現(xiàn)流程

    讀萬卷書不如行萬里路,只學書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+html+jdbc+mysql實現(xiàn)一個醫(yī)院門診收費管理系統(tǒng),大家可以在過程中查缺補漏,提升水平
    2021-11-11
  • Java實現(xiàn)讀取CSV文件并將數(shù)據(jù)放入對象

    Java實現(xiàn)讀取CSV文件并將數(shù)據(jù)放入對象

    CSV文件是一種常見的數(shù)據(jù)存儲格式,廣泛應用于數(shù)據(jù)交換,日志記錄和表格數(shù)據(jù)處理,在Java開發(fā)中,讀取CSV文件并將數(shù)據(jù)映射到對象中是常見的需求,本文將詳細介紹幾種常見方法,有需要的可以了解下
    2025-06-06
  • 詳解多線程及Runable 和Thread的區(qū)別

    詳解多線程及Runable 和Thread的區(qū)別

    這篇文章主要介紹了多線程及Runable 和Thread的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04

最新評論

洛阳市| 东丽区| 奉节县| 定南县| 诸暨市| 云和县| 苗栗市| 义乌市| 城市| 武隆县| 栾川县| 吐鲁番市| 华坪县| 祥云县| 兴和县| 平陆县| 平罗县| 弥勒县| 松溪县| 临朐县| 嘉峪关市| 铁岭县| 左贡县| 阿尔山市| 通渭县| 宣城市| 乐都县| 扎兰屯市| 东乌珠穆沁旗| 吴忠市| 阿图什市| 满洲里市| 老河口市| 高尔夫| 临安市| 汉寿县| 利津县| 兰溪市| 江安县| 芜湖市| 新郑市|