項目實踐 之 pdf簡歷的解析和填充(若依+vue3)

文章目錄

  • 環境背景
  • 最終效果
  • 前端講解
    • 左側模塊解析
    • 右側上傳模塊解析
    • 前端步驟
  • 后端講解
  • 代碼
    • 前端

環境背景

  • 若依前后端分離框架 + vue
  • 最后邊附有代碼哦

最終效果

在這里插入圖片描述

在這里插入圖片描述

前端講解

左側模塊解析

  • 1、左側表單使用el-form
    在這里插入圖片描述

    注意:
    1、prop出現的字段,需要保證是該類所具有的字段
    2、點擊提交按鈕后,調用的是handleSubmit方法

右側上傳模塊解析

在這里插入圖片描述

① v-if=“uploadedFileName” 如果對uploadedFileName不為空,該控件顯示
② v-model=“upload.open” 在vue2中會寫成 :visible.sync=“upload.open” ,在vue3中是不生效的,需要修改
③ 上傳文件限制,只能上傳1個
④ 前端限制,上傳的文件只能是pdf

前端步驟

  • 1、在打開頁面時,通過 created() 的 this.fetchResumeData()來獲取數據
    在這里插入圖片描述

  • 2、fetchResumeData通過await getResumeByUsername(username)來調用js的方法然后獲得數據,然后通過this.myResume=response.data填充
    在這里插入圖片描述

  • 3、當點擊上傳簡歷按鈕時,會調用handleImport方法,然后更改upload的open屬性為true,這樣就顯示了上傳文件的對話框了
    在這里插入圖片描述
    在這里插入圖片描述

  • 4、文件上傳完成后,會調用submitFileForm方法,開始上傳,同時調用upload中的url進行文件解析
    在這里插入圖片描述
    在這里插入圖片描述
    在這里插入圖片描述

  • 5、上傳成功后,會調用handleFileSuccess方法,然后將內容填充
    在這里插入圖片描述
    在這里插入圖片描述

后端講解

  • pom文件
<dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.26</version>
</dependency>
  • controller層
@RequestMapping("/parsepdf")public AjaxResult parsePdf(@RequestParam("file") MultipartFile file) {try {// 保存文件到本地String filePath = PdfUtil.save(file);// 獲取 PDF 文件內容String content = PdfUtil.getContent(file.getInputStream());// 解析 PDF 內容并封裝為簡歷信息Map<String, String> map = PdfUtil.setResume(content,file.getName(),filePath);// 返回解析后的數據return AjaxResult.success(map);} catch (Exception e) {System.err.println(e.getMessage());return AjaxResult.error("文件解析失敗:" + e.getMessage());}}
  • pdf格式說明

    需按照如下的格式,因為正則匹配的解析是這么來的,可以結合后邊的正則函數查看
    在這里插入圖片描述

  • PdfUtil類

package com.ruoyi.utils;import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.file.FileUploadUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.springframework.web.multipart.MultipartFile;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class PdfUtil {/*** 將上傳的文檔保存到本地* @param file* @return* @throws IOException*/public static String save(MultipartFile file) throws IOException{String path = FileUploadUtils.upload(file);// 為什么是e?String realPath = path.substring(path.indexOf("e")+2);String baseDir = RuoYiConfig.getProfile();String filePath = baseDir + realPath;return filePath;}public static String getContent(InputStream inputStream) throws IOException {try (PDDocument document = PDDocument.load(inputStream)) {PDFTextStripper stripper = new PDFTextStripper();return stripper.getText(document);}}/*** 將內容按照字段存儲進行匹配* @param content* @return*/public static Map<String,String> setResume(String content,String fileName,String filePath){// map用來存儲解析到的內容Map<String,String> map = new HashMap<>();map.put("file_name",fileName);map.put("file_path",filePath);String skillRegex ="專業技能\\s+(.*?)(?=工作經歷|$)"; ;  // "專業技能\r?\n([\s\S]+)"String skill = regex(skillRegex,content);System.err.println("--------------專業技能-------------");System.err.println("skills:"+skill);map.put("skills",skill);String phoneRegex = "聯系方式:(\\d+) 郵箱:(\\S+)";String phone = regex(phoneRegex,content);System.err.println("--------------聯系電話-------------");System.err.println("phone"+phone);map.put("phone",phone);String titleRegex = "求職意向\\s+(.*?)(?=簡介|$)";String title = regex(titleRegex,content);System.err.println("--------------求職意向-------------");System.err.println("title"+title);map.put("title",title);String summaryRegex =  "簡介\\s+(.*?)(?=獲獎及證書|$)";String summary = regex(summaryRegex,content);System.err.println("--------------簡介即總結-------------");System.err.println("summary"+summary);map.put("summary",summary);String experienceRegex = "工作經歷\\s+(.*?)(?=工作項目經歷|$)";// "工作項目經歷\\r?\\n([\\s\\S]+)"String experience = regex(experienceRegex,content);System.err.println("--------------工作項目經歷-------------");System.err.println("experience"+experience);map.put("experience",experience);String projectRegex = "工作項目經歷\\s+(.*)";// "工作項目經歷\\r?\\n([\\s\\S]+)"String project = regex(projectRegex,content);System.err.println("--------------工作項目經歷-------------");System.err.println("content"+project);map.put("content",project);String educationRegex = "教育經歷\\s+(.*)"; // "< < < 個人信息\\s*(.*?)(?=< < < 教育背景)"String education = regex(educationRegex,content);System.err.println("--------------教育背景-------------");System.err.println("education"+education);map.put("education",education);String certificationRegex = "獲獎及證書\\s+(.*?)(?=專業技能|$)";String certification = regex(certificationRegex,content);System.err.println("--------------獲獎及證書-------------");System.err.println("certifications"+certification);map.put("certifications",certification);return map;}/*** 匹配規則* @param regex 匹配要求* @param content  需要匹配的內容* @return 匹配結果*/public static String regex(String regex,String content){Pattern pattern=Pattern.compile(regex,Pattern.DOTALL);// 如果想要獲取多行,這里一定添加的是Pattern.DOTALLMatcher matcher=pattern.matcher(content);if(matcher.find()){String data=matcher.group(1).trim();return data;}return null;}
}

代碼

前端

<template><div class="container"><div class="my-myResume"><!--簡歷編輯頁面--><el-form :model="myResume" ref="resumeForm" label-width="120px" class="myResume-form"><el-form-item label="求職意向" prop="title"><el-input v-model="myResume.title" placeholder="請輸入簡歷標題"></el-input></el-form-item><el-form-item label="聯系方式A" prop="summary"><el-input type="textarea" v-model="myResume.phone" placeholder="請輸入您的手機號碼"></el-input></el-form-item><el-form-item label="個人介紹" prop="summary"><el-input type="textarea" v-model="myResume.summary" placeholder="請輸入個人介紹"></el-input></el-form-item><el-form-item label="工作經歷" prop="experience"><el-input type="textarea" v-model="myResume.experience" placeholder="請輸入工作經歷"></el-input></el-form-item><el-form-item label="工作項目經歷" prop="content"><el-input type="textarea" v-model="myResume.content" placeholder="請輸入工作項目經歷"></el-input></el-form-item><el-form-item label="教育經歷" prop="education"><el-input type="textarea" v-model="myResume.education" placeholder="請輸入教育經歷"></el-input></el-form-item><el-form-item label="專業技能" prop="skills"><el-input type="textarea" v-model="myResume.skills" placeholder="請輸入專業技能"></el-input></el-form-item><el-form-item label="獲獎及證書" prop="certifications"><el-input type="textarea" v-model="myResume.certifications" placeholder="請輸入獲得的認證"></el-input></el-form-item><el-button type="primary" @click="handleSubmit">提交</el-button></el-form></div><div class="pdfModule"><!--      上傳PDF按鈕--><el-button type="primary" @click="handleImport">上傳PDF簡歷</el-button><div v-if="uploadedFileName" class="file-name">已上傳文件:{{ uploadedFileName }}</div><!--      上傳對話框--><el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body="true"><el-uploadref="upload":limit="1"accept=".pdf":headers="upload.headers":action="upload.url":disabled="upload.isUploading":on-progress="handleFileUploadProgress":on-success="handleFileSuccess":auto-upload="false"><i class="el-icon-upload"></i><div class="el-upload__text">將文件拖到此處,或 <em>點擊上傳</em></div><div class="el-upload__tip text-center" slot="tip"><span>僅允許導入pdf格式文件</span></div></el-upload><div slot="footer" class="dialog-footer"><el-button type="primary" @click="submitFileForm">確定</el-button><el-button @click="upload.open = false">取消</el-button></div></el-dialog></div></div>
</template><script>
import {ElForm, ElFormItem, ElInput, ElButton, ElMessage} from 'element-plus';
import {updateResume, getResumeByUsername, uploadResume, getUserIdByUsername} from '@/api/myResume/myResume';  // 更新API路徑import Cookies from 'js-cookie'import axios from 'axios';import {getToken} from "@/utils/auth.js";export default {name: 'MyResume',data() {return {// 初始化簡歷對象myResume: {resume_id: null,  //初始化為null,后續從當前用戶獲取title: '',phone:'',summary: '',experience: '',content:'',education: '',skills: '',certifications: '',file_name:'',file_path:'',},// 簡歷導入參數upload:{open:false,title:"上傳PDF簡歷",isUploading:false,headers:{ Authorization:"Bearer "+getToken()},// url:process.env.VUE_APP_BASE_API+"/resumes/resume/import"url:"http://localhost:8088/student/myResume/parsepdf"},uploadedFileName:'', // 存儲上傳的文件名稱};},methods: {// 導入按鈕操作/*** 打開上傳對話框*/handleImport(){this.upload.title ="上傳PDF簡歷";this.upload.open = true;},/*** 文件上傳中處理*/handleFileUploadProgress(event,file,fileList){this.upload.isUploading = true;console.log("文件上傳中", event, file, fileList);},/*** 文件上傳成功處理*/async handleFileSuccess(response,file){this.upload.open = false;this.upload.isUploading = false;this.$refs.upload.clearFiles();if(response.code===200){this.fillFormWithPDFData(response.data);// 將解析的數據填充到表單中this.uploadedFileName = file.name;  //顯示上傳的文件名稱ElMessage.success('文件上傳成功');}else{ElMessage.error('文件解析失敗');}},/*** 提交上傳的文件*/submitFileForm(){console.log("上傳接口 URL:", this.upload.url); // 調試日志this.$refs.upload.submit();},// 將解析的PDF數據填充到表單中fillFormWithPDFData(data) {this.myResume.title = data.title || '';this.myResume.phone = data.phone || '';this.myResume.summary = data.summary || '';this.myResume.experience = data.experience || '';this.myResume.education = data.education || '';this.myResume.skills = data.skills || '';this.myResume.certifications = data.certifications || '';this.myResume.content = data.content || '';this.myResume.file_name = data.file_name || '';this.myResume.file_path = data.file_path || '';},// 提交表單更新簡歷async handleSubmit() {try {const username = this.getCurrentUsername(); // 獲取當前用戶的usernameconsole.log(username);if(!username){this.$message.error('未獲取到用戶信息');return;}const res = await updateResume(this.myResume);// 調用更新簡歷的APIconst userId = await getUserIdByUsername(username);console.log(userId);// const res = await  axios.post(url, this.myResume);if (res.code === 200) {ElMessage.success('簡歷更新成功');} else {ElMessage.success('簡歷更新失敗');}} catch (error) {console.error('提交失敗:', error);ElMessage.success('簡歷更新失敗');}},// 獲取簡歷數據 (初始加載)async fetchResumeData() {try {const username = await this.getCurrentUsername();const response = await getResumeByUsername(username);  // 調用獲取簡歷數據的方法if (response.code === 200) {this.myResume = response.data;  // 使用返回的數據更新 myResumethis.uploadedFileName = this.myResume.file_name;// 顯示已上傳的文件名稱if(this.uploadedFileName){this.upload.open = true;}} else {console.error('獲取簡歷數據失敗:', response.msg);}} catch (error) {console.error('請求失敗:', error);}},// 獲取當前用戶的usernamegetCurrentUsername(){const name = Cookies.get('username');return name;// return this.$store.state.user.userId;// return localStorage.getItem('userId');}},created() {this.fetchResumeData(); // 頁面加載時獲取簡歷數據}};
</script><style scoped>/* 容器布局 */
.container {display: flex;width: 100%;height: 100vh; /* 使容器占滿整個視口高度 */background-color: #f9f9f9; /* 淺灰色背景 */
}/* 簡歷編輯區域 */
.my-myResume {flex: 7; /* 占據 4 份 */padding: 20px;overflow-y: auto; /* 如果內容過多,允許滾動 */
}/* PDF上傳區域 */
.pdfModule {flex: 2; /* 占據 1 份 */padding: 20px;/*border-left: 1px solid #ddd; !* 添加左邊框分隔 *!*/
}/* 表單樣式 */
.myResume-form {max-width: 800px; /* 限制表單最大寬度 */margin: 0 auto; /* 居中顯示 */
}.file-name{margin-top: 10px;font-size: 14px;color:#666;
}.el-upload__text {font-size: 14px;color: #666;
}.el-upload__tip {font-size: 12px;color: #999;
}.dialog-footer {text-align: right;
}
</style>
  • js內容
// src/myResume/myResume.jsimport request from '@/utils/request'
// 根據username獲取userId
export function getUserIdByUsername(username){return request({url:`/student/myResume/getUserId/${username}`,method:'get'})
}
// 更新簡歷數據
export function updateResume(data) {return request({url: '/student/myResume/updateResume',method: 'put',data: data})
}// 根據username獲取簡歷數據
export function getResumeByUsername(username) {return request({url: `/student/myResume/getByUsername/${username}`,method: 'get',})
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/71934.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/71934.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/71934.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Electron入門】進程環境和隔離

目錄 一、主進程和渲染進程 1、主進程&#xff08;main&#xff09; 2、渲染進程&#xff08;renderer&#xff09; 二、預加載腳本 三、沙盒化 為單個進程禁用沙盒 全局啟用沙盒 四、環境訪問權限控制&#xff1a;contextIsolation和nodeIntegration 1、contextIsola…

如何看待 Kaiming He 最新提出的 Fractal Generative Models ?

何愷明團隊提出的分形生成模型(Fractal Generative Models) 引發了廣泛關注,其核心思想是通過遞歸調用生成模型模塊構建自相似結構,類似數學中的分形概念(如雪花結構),從而高效生成高分辨率數據(如圖像)。 Fractal Generative Models即分形生成模型,是一種新型的生成…

Debian系統終端輸入ifconfig報錯

報錯 bash: ifconfig: command not found 原因 Debian 上默認不安裝 ifconfig 軟件包。這是因為 ifconfig 已被棄用&#xff0c;取而代之的是新的 ip 命令。該 ip 命令現在負責修改或顯示路由、網絡設備、接口和隧道 如果仍然想使用舊的 ifconfig 命令&#xff0c;則必須顯式…

【NLP 27、文本分類任務 —— 傳統機器學習算法】

不要抓著枯葉哭泣&#xff0c;你要等待初春的新芽 —— 25.1.23 一、文本分類任務 定義&#xff1a;預先設定好一個文本類別集合&#xff0c;對于一篇文本&#xff0c;預測其所屬的類別 例如&#xff1a; 情感分析&#xff1a; 這家飯店太難吃了 —> 正類 …

Lumoz Chain正式上線:AI 時代的新算力破局者

新的敘事和技術突破永遠是推動行業前行的核心動力。當下&#xff0c;AI Agent無疑是最炙手可熱的賽道之一。 當加密世界將目光投向AI領域時&#xff0c;大多數項目仍停留在以AI為工具或應用場景的層面&#xff0c;試圖通過集成AI模型或優化鏈上功能來吸引用戶。然而&#xff0c…

Python - Python連接數據庫

Python的標準數據庫接口為&#xff1a;Python DB-API&#xff0c;Python DB-API為開發人員提供了數據庫應用編程接口。 PyMySQL 是在 Python3.x 版本中用于連接 MySQL 服務器的一個實現庫&#xff0c;Python2中則使用mysqldb。 PyMySQL 遵循 Python 數據庫 API v2.0 規范&…

面試八股文--數據庫基礎知識總結(1)

1、數據庫的定義 數據庫&#xff08;DataBase&#xff0c;DB&#xff09;簡單來說就是數據的集合數據庫管理系統&#xff08;Database Management System&#xff0c;DBMS&#xff09;是一種操縱和管理數據庫的大型軟件&#xff0c;通常用于建立、使用和維護數據庫。數據庫系統…

關于在java項目部署過程MySQL拒絕連接的分析和解決方法

前言 在最近一次部署項目一次項目部署過程中&#xff0c;由于沒有對MySQL數據庫的部分權限和遠程連接進行授權&#xff0c;導致了在執行項目功能API時&#xff0c;出現MySQL連接異常或MySQL拒絕連接的問題。 問題 以下是部分報錯截圖&#xff1a; 分析 根據日志提示&#xf…

PhotoLine綠色版 v25.00:全能型圖像處理軟件的深度解析

在圖像處理領域,PhotoLine以其強大的功能和緊湊的體積,贏得了國內外眾多用戶的喜愛。本文將為大家全面解析PhotoLine綠色版 v25.00的各項功能,幫助大家更好地了解這款全能型的圖像處理軟件。 一、迷你體積,強大功能 PhotoLine被譽為迷你版的Photoshop,其體積雖小,但功能卻…

阿里重磅模型深夜開源;DeepSeek宣布開源DeepGEMM;微軟開源多模態AI Agent基礎模型Magma...|網易數智日報

阿里重磅模型深夜開源&#xff1a;表現超越Sora、Pika&#xff0c;消費級顯卡就能跑 2月26日&#xff0c;25日深夜阿里云視頻生成大模型萬相2.1&#xff08;Wan&#xff09;正式宣布開源&#xff0c;此次開源采用Apache2.0協議&#xff0c;14B和1.3B兩個參數規格的全部推理代碼…

002 Java操作kafka客戶端

Java操作kafka客戶端 文章目錄 Java操作kafka客戶端3.Java操作kafka客戶端1.引入依賴2. Kafka服務配置3、生產者&#xff08;Producer&#xff09;實現1. 基礎配置與發送消息2. 關鍵配置說明 4.消費者&#xff08;Consumer&#xff09;實現1. 基礎配置與消費消息2. 關鍵配置說明…

【SRC實戰】信息泄露導致越權會員功能

01 — 漏洞證明 1、VIP功能 2、SVIP功能 3、點擊任意用戶發起私聊&#xff0c;發現userId純數字可遍歷 4、返回包泄露身高范圍height&#xff0c;星座constellation&#xff0c;屬相zodiac&#xff0c;戀愛目標purpose&#xff0c;教育程度degree&#xff0c;成功越權VIP功能 …

游戲引擎學習第125天

倉庫:https://gitee.com/mrxiao_com/2d_game_3 回顧并為今天的內容做準備。 昨天&#xff0c;當我們離開時&#xff0c;工作隊列已經完成了基本的功能。這個隊列雖然簡單&#xff0c;但它能夠執行任務&#xff0c;并且我們已經為各種操作編寫了測試。字符串也能夠正常推送到隊…

藍橋杯 Java B 組之記憶化搜索(滑雪問題、斐波那契數列)

Day 5&#xff1a;記憶化搜索&#xff08;滑雪問題、斐波那契數列&#xff09; &#x1f4d6; 一、記憶化搜索簡介 記憶化搜索&#xff08;Memoization&#xff09; 是一種優化遞歸的方法&#xff0c;它利用 哈希表&#xff08;HashMap&#xff09;或數組 存儲已經計算過的結果…

反爬蟲策略

反爬蟲策略是網站用于防止自動化程序&#xff08;爬蟲&#xff09;惡意抓取數據的核心手段&#xff0c;其設計需兼顧有效性、用戶體驗和合法性。 一、 基礎檢測與攔截 User-Agent檢測&#xff1a;驗證請求頭中的User-Agent&#xff0c;攔截非常見或已知爬蟲標識。IP頻率限制&…

Java 實現快速排序算法:一條快速通道,分而治之

大家好&#xff0c;今天我們來聊聊快速排序&#xff08;QuickSort&#xff09;算法&#xff0c;這個經典的排序算法被廣泛應用于各種需要高效排序的場景。作為一種分治法&#xff08;Divide and Conquer&#xff09;算法&#xff0c;快速排序的效率在平均情況下非常高&#xff…

深入解析 Spring 中的 BeanDefinition 和 BeanDefinitionRegistry

在 Spring 框架中&#xff0c;BeanDefinition 和 BeanDefinitionRegistry 是兩個非常重要的概念&#xff0c;它們共同構成了 Spring IoC 容器的核心機制。本文將詳細介紹這兩個組件的作用、實現以及它們之間的關系。 一、BeanDefinition&#xff1a;Bean 的配置描述 1.1 什么…

《OpenCV》——光流估計

什么是光流估計&#xff1f; 光流估計的前提&#xff1f; 基本假設 亮度恒定假設&#xff1a;目標像素點的亮度在相鄰幀之間保持不變。這是光流計算的基礎假設&#xff0c;基于此可以建立數學方程來求解光流。時間連續或運動平滑假設&#xff1a;相鄰幀之間的時間間隔足夠小&a…

信息系統的安全防護

文章目錄 引言**1. 物理安全****2. 網絡安全****3. 數據安全****4. 身份認證與訪問控制****5. 應用安全****6. 日志與監控****7. 人員與管理制度****8. 其他安全措施****9. 安全防護框架**引言 從技術、管理和人員三個方面綜合考慮,構建多層次、多維度的安全防護體系。 信息…