• ruoyi实现富文本生成网页功能,vue实现wangEditro功能,网页静态化,二维码预览网页


    先看效果

     

     

     

     废话不多直接上代码,也不墨迹了,代码自己拿去跑,前段代码没什么依赖,可以直接跑起来

    注意点

    1,:需要安装QRCode 组件,自己百度去

    2:需要安装wangEditor富文本编辑器组件,版本5,请上官网自己找教程,找vue版本的,安装很复杂,貌似还有兼容问题,如果有问题在回复我,我用的是ruoyi3.7.0

    1. <template>
    2. <div class="app-container">
    3. <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
    4. <el-form-item label="标题" prop="picNm">
    5. <el-input v-model="queryParams.picNm" size="small" clearable>el-input>
    6. el-form-item>
    7. <el-form-item label="投放时间" prop="releaseTime">
    8. <el-date-picker clearable size="small"
    9. v-model="queryParams.releaseTime"
    10. type="date"
    11. value-format="yyyy-MM-dd"
    12. >
    13. el-date-picker>
    14. el-form-item>
    15. <el-form-item>
    16. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索el-button>
    17. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置el-button>
    18. el-form-item>
    19. el-form>
    20. <el-form label-width="68px">
    21. <el-row :gutter="10" class="mb8">
    22. <el-col :span="1.5">
    23. <el-button
    24. type="primary"
    25. plain
    26. size="mini"
    27. @click="handleAdd"
    28. v-hasPermi="['web:lunbo:list']"
    29. >新增
    30. el-button>
    31. el-col>
    32. <el-col :span="1.5">
    33. <el-button
    34. type="success"
    35. plain
    36. size="mini"
    37. :disabled="single"
    38. @click="handleUpdate"
    39. v-hasPermi="['web:lunbo:list']"
    40. >修改
    41. el-button>
    42. el-col>
    43. <el-col :span="1.5">
    44. <el-button
    45. type="danger"
    46. plain
    47. size="mini"
    48. :disabled="multiple"
    49. @click="handleDelete"
    50. v-hasPermi="['web:lunbo:list']"
    51. >删除
    52. el-button>
    53. el-col>
    54. el-row>
    55. el-form>
    56. <el-table border v-loading="loading" :data="lunboList" @selection-change="handleSelectionChange">
    57. <el-table-column type="selection" width="55" align="center"/>
    58. <el-table-column label="标题" align="left" prop="picNm" :show-overflow-tooltip="true"/>
    59. <el-table-column label="封面图片" align="center" prop="picAddr" width="150">
    60. <template slot-scope="scope">
    61. <el-image
    62. style="height: 35px;padding: 0"
    63. fit="fill"
    64. :src="conf.imgUrlDeal(scope.row.picAddr)"
    65. >
    66. el-image>
    67. template>
    68. el-table-column>
    69. <el-table-column label="描述" align="left" prop="picDesc" width="160" :show-overflow-tooltip="true"/>
    70. <el-table-column label="是否展示" align="center" prop="picShow" width="110">
    71. <template slot-scope="scope">
    72. <el-switch
    73. class="switch"
    74. v-model="scope.row.picShow"
    75. :validate-event="true"
    76. active-text="显示"
    77. inactive-text="隐藏"
    78. active-value="0"
    79. inactive-value="1"
    80. @change="isShowTypeChange(scope.row)"
    81. />
    82. template>
    83. el-table-column>
    84. <el-table-column label="广告链接" align="left" prop="picHyperlink" :show-overflow-tooltip="true">
    85. <template slot-scope="scope">
    86. <span @click="conf.openIframe(scope.row.picHyperlink,show,urlItem)">
    87. <el-link type="primary" style="font-size: 11px" target="_blank">{{ scope.row.picHyperlink }}el-link>
    88. span>
    89. template>
    90. el-table-column>
    91. <el-table-column label="更新人" align="center" prop="modifier" width="90"/>
    92. <el-table-column label="更新时间" align="center" prop="modifyDt" width="125">
    93. <template slot-scope="scope">
    94. <span>{{ parseTime(scope.row.modifyDt, '{y}-{m}-{d}') }}span>
    95. template>
    96. el-table-column>
    97. <el-table-column label="投放开始时间" align="center" prop="modifyDt" width="125">
    98. <template slot-scope="scope">
    99. <span>{{ parseTime(scope.row.releaseTime, '{y}-{m}-{d}') }}span>
    100. template>
    101. el-table-column>
    102. <el-table-column label="投放结束时间" align="center" prop="modifyDt" width="125">
    103. <template slot-scope="scope">
    104. <span>{{ parseTime(scope.row.lockTime, '{y}-{m}-{d}') }}span>
    105. template>
    106. el-table-column>
    107. <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="160">
    108. <template slot-scope="scope">
    109. <el-button slot="reference" size="mini" type="text"
    110. @click="creatQrCode(scope.row.picHyperlink)"
    111. > 预览
    112. el-button>
    113. <el-button
    114. size="mini"
    115. type="text"
    116. icon="el-icon-edit"
    117. @click="handleUpdate(scope.row)"
    118. v-hasPermi="['web:lunbo:list']"
    119. >修改
    120. el-button>
    121. <el-button
    122. size="mini"
    123. type="text"
    124. icon="el-icon-delete"
    125. @click="handleDelete(scope.row)"
    126. v-hasPermi="['web:lunbo:list']"
    127. >删除
    128. el-button>
    129. template>
    130. el-table-column>
    131. el-table>
    132. <pagination
    133. v-show="total>0"
    134. :total="total"
    135. :page.sync="queryParams.pageNum"
    136. :limit.sync="queryParams.pageSize"
    137. @pagination="getList"
    138. />
    139. <el-dialog :title="title" :visible.sync="open" width="1000px" append-to-body>
    140. <el-form :inline="true" ref="form" :model="form" :rules="rules" label-width="100px">
    141. <el-row>
    142. <el-col :span="12">
    143. <el-form-item label="标题" prop="picNm">
    144. <el-input style="width: 360px" @change="updateRichHtml" v-model="form.picNm"/>
    145. el-form-item>
    146. <el-form-item label="描述" prop="picDesc">
    147. <el-input style="width: 360px" v-model="form.picDesc"/>
    148. el-form-item>
    149. <el-form-item label="是否显示" prop="picShow">
    150. <el-checkbox-group v-model="form.picShow"
    151. @change="((val)=> {
    152. form.picShow = (val == 'null' || !val) ? nextItem.picShow : val;
    153. nextItem.picShow = form.picShow;
    154. })"
    155. >
    156. <el-checkbox false-label="null" true-label="0">显示el-checkbox>
    157. <el-checkbox false-label="null" true-label="1">隐藏el-checkbox>
    158. el-checkbox-group>
    159. el-form-item>
    160. <el-form-item label="网页类型" prop="pageType">
    161. <el-checkbox-group v-model="form.pageType"
    162. @change="((val)=> {
    163. form.pageType = (val == 'null' || !val) ? nextItem.pageType : val;
    164. nextItem.pageType = form.pageType;
    165. show.richText = form.pageType == '0'
    166. })"
    167. >
    168. <el-checkbox false-label="null" true-label="1">第三方el-checkbox>
    169. <el-checkbox false-label="null" true-label="0">本地页面el-checkbox>
    170. el-checkbox-group>
    171. el-form-item>
    172. el-col>
    173. <el-col :span="12">
    174. <el-form-item label="封面图片" prop="picAddr">
    175. <div class="avatar-uploader">
    176. <el-upload style="width: 360px" action="#" accept=".jpg,.png,.jpeg" :http-request="requestUpload"
    177. :show-file-list="false"
    178. :before-upload="beforeUpload"
    179. >
    180. <el-image v-if="form.picNmFile" fit="contain" style="max-width: 100%" :src="form.picAddr"
    181. class="avatar"
    182. />
    183. <el-image v-else-if="form.picAddr" fit="contain" style="max-width: 100%" :src="baseUrl + form.picAddr"
    184. class="avatar"
    185. />
    186. <i v-else class="el-icon-plus avatar-uploader-icon">i>
    187. el-upload>
    188. <span style="color: #999999;font-size: 11px">{{ routeQueryParms.picstr }}span>
    189. div>
    190. el-form-item>
    191. el-col>
    192. el-row>
    193. <el-row>
    194. <el-col :span="12">
    195. <el-form-item label="跳转链接" prop="picHyperlink">
    196. <el-input type="textarea" style="width: 360px" v-model="form.picHyperlink"/>
    197. el-form-item>
    198. el-col>
    199. <el-col :span="12">
    200. <el-form-item label="备注" prop="remarks">
    201. <el-input v-model="form.remarks" style="width: 360px" type="textarea">el-input>
    202. el-form-item>
    203. el-col>
    204. el-row>
    205. <el-row>
    206. <el-col :span="12">
    207. <el-form-item label="发布时间" prop="releaseTime">
    208. <el-date-picker
    209. style="width: 360px"
    210. size="small"
    211. value-format="yyyy-MM-dd HH:mm:ss"
    212. v-model="form.releaseTime"
    213. type="daterange"
    214. align="right"
    215. unlink-panels
    216. range-separator="至"
    217. start-placeholder="发布时间"
    218. end-placeholder="截止时间"
    219. :picker-options="conf.pickerOptions"
    220. >
    221. el-date-picker>
    222. el-form-item>
    223. el-col>
    224. el-row>
    225. <div style="display: flex;justify-content: space-between" v-show="show.richText">
    226. <div style="width: 100px;text-align: right;padding-right: 15px">
    227. 网页内容
    228. div>
    229. <div style="border: 1px solid #ccc;width: 98%;z-index: 9999">
    230. <Toolbar
    231. class="toolbar_1"
    232. :editor="editorItem.editor"
    233. :defaultConfig="editorItem.toolbarConfig"
    234. :mode="editorItem.mode"
    235. />
    236. <Editor
    237. ref="editor"
    238. style="height:410px; overflow-y: scroll;width: 100%;background-color:white;"
    239. v-model="form.richText"
    240. :defaultConfig="editorItem.editorConfig"
    241. :mode="editorItem.mode"
    242. @onCreated="onCreated"
    243. />
    244. div>
    245. div>
    246. el-form>
    247. <div slot="footer" class="dialog-footer">
    248. <el-button type="primary" @click="submitForm">确 定el-button>
    249. <el-button @click="cancel">取 消el-button>
    250. div>
    251. el-dialog>
    252. <el-dialog
    253. title="扫码预览"
    254. :visible.sync="show.qrCode"
    255. width="400px"
    256. top="50vh"
    257. >
    258. <div style="display: flex;justify-content: center;">
    259. <div class="qrcode" ref="qrcode">div>
    260. div>
    261. el-dialog>
    262. <el-dialog
    263. title="预览网页"
    264. :visible.sync="show.html"
    265. width="500px"
    266. height="950px"
    267. @close="()=>{urlItem.htmlUrl = null}"
    268. >
    269. <div style="display: flex;justify-content: center;">
    270. <iframe :src="urlItem.htmlUrl" height="800" width="550" title="广告预览" frameborder="0">iframe>
    271. div>
    272. el-dialog>
    273. div>
    274. template>
    275. <script>
    276. import { listLunbo, getLunbo, delLunbo, addLunbo, updateLunbo, exportLunbo, picShowUpdate } from '@/api/gzApi/lunbo'
    277. // import { getToken } from '@/utils/auth'
    278. import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
    279. import '@wangeditor/editor/dist/css/style.css'
    280. import editorConfig from '@/utils/wangEditor'
    281. import { pickerOptions, openIframe, imgUrlDeal, getNowDate } from '@/utils/elementuiConf'
    282. import { DomEditor } from '@wangeditor/editor'
    283. // 配置编辑器是否支持滚动,默认为 true
    284. import QRCode from 'qrcodejs2'
    285. export default {
    286. // dom中的editor
    287. components: { Editor, Toolbar },
    288. dicts: ['isok'],
    289. name: 'Lunbo',
    290. data() {
    291. return {
    292. urlItem: {
    293. htmlUrl: null
    294. },
    295. conf: {
    296. pickerOptions: pickerOptions,
    297. openIframe: openIframe,
    298. imgUrlDeal: imgUrlDeal
    299. },
    300. show: {
    301. richText: false,
    302. qrCode: false,
    303. html: false
    304. },
    305. nextItem: {
    306. picShow: null,
    307. pageType: '1',
    308. contentTitle: '{#标题#}'
    309. },
    310. editorItem: {
    311. editor: null,
    312. html: '',
    313. // 工具栏配置
    314. // toolbarConfig:,
    315. editorConfig: editorConfig,
    316. mode: 'default' // or 'simple'
    317. },
    318. // headerObj: {
    319. // Authorization: 'Bearer ' + getToken()
    320. // },
    321. routeQueryParms: {},
    322. // 后台地址
    323. baseUrl: process.env.VUE_APP_BASE_API,
    324. // 遮罩层
    325. loading: true,
    326. // 导出遮罩层
    327. exportLoading: false,
    328. // 选中数组
    329. ids: [],
    330. picNms: [],
    331. // 非单个禁用
    332. single: true,
    333. // 非多个禁用
    334. multiple: true,
    335. // 显示搜索条件
    336. showSearch: true,
    337. // 总条数
    338. total: 0,
    339. // 广告轮播图表格数据
    340. lunboList: [],
    341. // 弹出层标题
    342. title: '',
    343. // 是否显示弹出层
    344. open: false,
    345. // 查询参数
    346. queryParams: {
    347. pageNum: 1,
    348. pageSize: 10,
    349. picShow: null,
    350. adType: null,
    351. pageType: null,
    352. picNm: null,
    353. releaseTime: null
    354. },
    355. // 表单参数
    356. form: { pageType: '1' },
    357. // 表单校验
    358. rules: {
    359. picNm: [
    360. { required: true, message: '标题不能为空', trigger: 'blur' }
    361. ],
    362. releaseTime: [
    363. { required: true, message: '请选择发布时间', trigger: 'blur' }
    364. ],
    365. picAddr: [
    366. { required: true, message: '请添加图片', trigger: 'blur' },
    367. {
    368. validator: (rule, value, callback) => {
    369. if (!this.form.picAddr) {
    370. return callback(new Error('请添加图片'))
    371. } else {
    372. callback()
    373. }
    374. },
    375. trigger: ['blur', 'change']
    376. }
    377. ],
    378. picDesc: [
    379. { required: true, message: '描述不能为空', trigger: 'blur' }
    380. ]
    381. }
    382. }
    383. },
    384. beforeDestroy() {
    385. const editor = this.editorItem.editor
    386. if (editor == null) return
    387. editor.destroy() // 组件销毁时,及时销毁编辑器
    388. },
    389. mounted() {
    390. },
    391. created() {
    392. const query = this.$route.query
    393. this.routeQueryParms = query
    394. this.getList()
    395. },
    396. methods: {
    397. isShowTypeChange(row) {
    398. this.$modal.confirm('是否' + (row.picShow == '0' ? '展示' : '隐藏') + '标题为"' + row.picNm + '"的数据项?').then(function() {
    399. return picShowUpdate({ id: row.id, picShow: row.picShow })
    400. }).then(() => {
    401. this.getList()
    402. this.$modal.msgSuccess('操作成功!')
    403. }).catch(function() {
    404. // 00 隐藏,01不隐藏
    405. row.picShow = (row.picShow === '0' ? '1' : '0')
    406. })
    407. },
    408. // 二维码方法
    409. creatQrCode(url) {
    410. this.$set(this.show, 'qrCode', true)
    411. this.$nextTick(() => {
    412. this.$refs.qrcode.innerHTML = ''
    413. const qrcode = new QRCode(this.$refs.qrcode, {
    414. text: url, // 需要转换为二维码的内容
    415. width: 180,
    416. height: 180,
    417. colorDark: '#000000',
    418. colorLight: '#ffffff',
    419. correctLevel: QRCode.CorrectLevel.H
    420. })
    421. })
    422. },
    423. onCreated(editor) {
    424. this.editorItem.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错
    425. },
    426. // 覆盖默认的上传行为
    427. requestUpload() {
    428. },
    429. beforeUpload(file) {
    430. const isLt2M = file.size / 1024 / 1024 < 2
    431. if (!isLt2M) {
    432. this.$message.error('图片大小不能超过2MB!')
    433. return
    434. }
    435. if (file.type.indexOf('image/') == -1) {
    436. this.$modal.msgError('文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。')
    437. } else {
    438. const reader = new FileReader()
    439. reader.readAsDataURL(file)
    440. reader.onload = () => {
    441. // 这个是要上传的真实图片文件
    442. this.form.picNmFile = file
    443. // 此时图片是base64编码文件
    444. this.form.picAddr = reader.result
    445. this.$refs.form.validateField('picAddr')
    446. }
    447. }
    448. },
    449. /** 查询广告轮播图列表 */
    450. getList() {
    451. this.loading = true
    452. listLunbo(this.queryParams).then(response => {
    453. this.lunboList = response.rows
    454. this.total = response.total
    455. this.loading = false
    456. })
    457. },
    458. // 取消按钮
    459. cancel() {
    460. this.open = false
    461. this.reset()
    462. },
    463. // 表单重置
    464. reset() {
    465. this.form = {
    466. id: null,
    467. picNm: null,
    468. picAddr: null, // 新增的时候是base64编码字节数据,但是只有在file不为空的时候才会提交
    469. picDesc: null,
    470. picShow: '0',
    471. creater: null,
    472. createDt: null,
    473. modifier: null,
    474. modifyDt: null,
    475. remarks: null,
    476. deleteFlag: null,
    477. picHyperlink: null,
    478. adType: null,
    479. pageType: '1', // 默认外部网页链接
    480. countdown: null,
    481. savepic: null,
    482. picNmFile: null, // 图片文件
    483. richText: '', // 富文本
    484. releaseTime: null,
    485. lockTime: null
    486. }
    487. this.show = {
    488. richText: false
    489. }
    490. this.resetForm('form')
    491. },
    492. /** 搜索按钮操作 */
    493. handleQuery() {
    494. this.queryParams.pageNum = 1
    495. this.getList()
    496. },
    497. /** 重置按钮操作 */
    498. resetQuery() {
    499. this.resetForm('queryForm')
    500. this.handleQuery()
    501. },
    502. // 多选框选中数据
    503. handleSelectionChange(selection) {
    504. this.ids = selection.map(item => item.id)
    505. this.picNms = selection.map(item => item.picNm)
    506. this.single = selection.length !== 1
    507. this.multiple = !selection.length
    508. },
    509. /** 新增按钮操作 */
    510. handleAdd() {
    511. this.reset()
    512. this.open = true
    513. this.title = '添加广告轮播图'
    514. // 清除富文本内容
    515. // if (this.editorItem.editor) {
    516. // this.editorItem.editor.clear();
    517. // this.editorItem.editor.setHtml('

      ')
    518. // }
    519. this.nextItem.contentTitle = '{#标题#}'
    520. this.$set(this.form, 'richText',
    521. '

      ' +

    522. '' + this.nextItem.contentTitle + '' +
    523. ''+
    524. '

      ' +

    525. '运营 ' + getNowDate() + '' +
    526. '

      '
    527. )
    528. },
    529. // 更新富文本中的标题
    530. updateRichHtml(val) {
    531. // 富文本内容
    532. let richText = this.form.richText
    533. // 占位符
    534. let contentTitle = this.nextItem.contentTitle
    535. // 如果不为初始值,那么就截取,真实值,方便替换富文本内容
    536. if ('{#标题#}' !== contentTitle) {
    537. contentTitle = contentTitle.replace('{#', '').replace('#}', '')
    538. }
    539. // 将标题替换进内容中
    540. const newHtml = richText.replace(contentTitle, val)
    541. this.$set(this.form, 'richText', newHtml)
    542. this.nextItem.contentTitle = '{#' + val + '#}'
    543. },
    544. /** 修改按钮操作 */
    545. handleUpdate(row) {
    546. this.reset()
    547. const id = row.id || this.ids
    548. getLunbo(id).then(response => {
    549. // 初始化发布时间
    550. if (response.data.releaseTime) {
    551. response.data.releaseTime = [response.data.releaseTime, response.data.lockTime]
    552. }
    553. this.form = response.data
    554. // 如果是本地连接则打开富文本编辑器,否则则关闭
    555. this.show.richText = this.form.pageType === '0'
    556. // 写入富文本内容
    557. this.$nextTick(() => {
    558. if (this.editorItem.editor) {
    559. this.editorItem.editor.clear()
    560. this.editorItem.editor.setHtml(this.form.richText)
    561. }
    562. })
    563. this.open = true
    564. this.title = '修改广告轮播图'
    565. })
    566. },
    567. /** 提交按钮 */
    568. submitForm() {
    569. this.$refs['form'].validate(valid => {
    570. let fd = new FormData()
    571. fd.append('id', this.form.id ? this.form.id : '')
    572. fd.append('picNm', this.form.picNm ? this.form.picNm : '')
    573. // 图片文件存在才去拼接
    574. if (this.form.picNmFile) {
    575. fd.append('picNmFile', this.form.picNmFile, this.form.picNmFile.name)
    576. }
    577. fd.append('picHyperlink', this.form.picHyperlink ? this.form.picHyperlink : '')
    578. fd.append('remarks', this.form.remarks ? this.form.remarks : '')
    579. fd.append('picDesc', this.form.picDesc ? this.form.picDesc : '')
    580. fd.append('picShow', this.form.picShow ? this.form.picShow : '')
    581. fd.append('createDt', this.form.createDt ? this.form.createDt : '')
    582. fd.append('modifyDt', this.form.modifyDt ? this.form.modifyDt : '')
    583. fd.append('pageType', this.form.pageType ? this.form.pageType : '')
    584. fd.append('richText', this.form.richText ? this.form.richText : '')
    585. // 发布时间
    586. if (this.form.releaseTime) {
    587. fd.append('lockTime', this.form.releaseTime ? this.form.releaseTime[1] : '')
    588. fd.append('releaseTime', this.form.releaseTime ? this.form.releaseTime[0] : '')
    589. }
    590. if (valid) {
    591. if (this.form.id != null) {
    592. updateLunbo(fd).then(response => {
    593. this.$modal.msgSuccess('修改成功')
    594. this.open = false
    595. this.getList()
    596. })
    597. } else {
    598. addLunbo(fd).then(response => {
    599. this.$modal.msgSuccess('新增成功')
    600. this.open = false
    601. this.getList()
    602. })
    603. }
    604. }
    605. })
    606. },
    607. /** 删除按钮操作 */
    608. handleDelete(row) {
    609. const ids = row.id || this.ids
    610. const names = row.picNm || this.picNms
    611. this.$modal.confirm('是否删除广告图标题为"' + names + '"的数据项?').then(function() {
    612. return delLunbo(ids)
    613. }).then(() => {
    614. this.getList()
    615. this.$modal.msgSuccess('删除成功')
    616. }).catch(() => {
    617. })
    618. },
    619. /** 导出按钮操作 */
    620. handleExport() {
    621. const queryParams = this.queryParams
    622. this.$modal.confirm('是否确认导出所有广告轮播图数据项?').then(() => {
    623. this.exportLoading = true
    624. return exportLunbo(queryParams)
    625. }).then(response => {
    626. this.$download.name(response.msg)
    627. this.exportLoading = false
    628. }).catch(() => {
    629. })
    630. }
    631. }
    632. }
    633. script>
    634. <style scoped>
    635. /*src="@wangeditor/editor/dist/css/style.css"*/
    636. .avatar-uploader .el-upload {
    637. border: 1px dashed #d9d9d9 !important;
    638. border-radius: 6px !important;
    639. cursor: pointer !important;
    640. position: relative !important;
    641. overflow: hidden !important;
    642. }
    643. .avatar-uploader .el-upload:hover {
    644. border-color: #409EFF !important;
    645. }
    646. .avatar-uploader-icon {
    647. font-size: 34px !important;
    648. color: #8c939d !important;
    649. width: 350px !important;
    650. height: 180px !important;
    651. line-height: 180px !important;
    652. text-align: center !important;
    653. border: 1px #d9d9d9 solid !important;
    654. }
    655. .avatar {
    656. /*width: 200px;*/
    657. height: 180px !important;
    658. /*display: block;*/
    659. }
    660. .el-form-item--medium .el-form-item__content {
    661. line-height: 0 !important;
    662. }
    663. ::v-deep .toolbar_1 .title {
    664. font-size: 10px;
    665. margin: 0;
    666. text-align: center;
    667. }
    668. .toolbar_1 {
    669. border-bottom: 1px solid #ccc;
    670. }
    671. /* switch按钮样式 */
    672. ::v-deep .switch .el-switch__label {
    673. position: absolute;
    674. display: none;
    675. color: #fff !important;
    676. }
    677. /*打开时文字位置设置*/
    678. ::v-deep .switch .el-switch__label--right {
    679. z-index: 1;
    680. }
    681. /* 调整打开时文字的显示位子 */
    682. ::v-deep .switch .el-switch__label--right span {
    683. margin-right: 9px;
    684. }
    685. /*关闭时文字位置设置*/
    686. ::v-deep .switch .el-switch__label--left {
    687. z-index: 1;
    688. }
    689. /* 调整关闭时文字的显示位子 */
    690. ::v-deep .switch .el-switch__label--left span {
    691. margin-left: 9px;
    692. }
    693. /*显示文字*/
    694. ::v-deep .switch .el-switch__label.is-active {
    695. display: block;
    696. }
    697. /* 调整按钮的宽度 */
    698. ::v-deep .switch.el-switch .el-switch__core,
    699. ::v-deep .el-switch .el-switch__label {
    700. width: 70px !important;
    701. margin: 0;
    702. }
    703. /*::v-deep .w-e-text-container .w-e-modal :first-child label:nth-of-type(3){*/
    704. /* background-color: #00afff;*/
    705. /* display: none;*/
    706. /*}*/
    707. ::v-deep video{
    708. max-width: 100%;
    709. }
    710. style>

    第二步,补齐配置文件

    上述vue组件中使用到的wangEditor.js配置,请依据引入的路径创建本文件
    
    1. import { getToken } from '@/utils/auth'
    2. // import resize from "../views/dashboard/mixins/resize";
    3. import { uploadFileHce } from '../api/common'
    4. import { Message } from 'element-ui'
    5. export default {
    6. // menus: [
    7. // // "head",
    8. // 'bold',
    9. // 'fontSize',
    10. // "fontName",
    11. // 'italic',
    12. // 'underline',
    13. // '|',
    14. // 'strikeThrough',
    15. // 'indent',
    16. // 'lineHeight',
    17. // 'foreColor',
    18. // 'backColor',
    19. // 'link',
    20. // '|',
    21. // 'list',
    22. // // "todo",
    23. // 'image',
    24. // 'justify',
    25. // 'quote',
    26. // 'table',
    27. // '|',
    28. // 'code',
    29. // 'splitLine',
    30. // 'undo',
    31. // 'emoticon',
    32. // 'redo'
    33. // ],
    34. placeholder: '请输入内容...',
    35. scroll: false,
    36. // autoFocus:true,
    37. MENU_CONF: {
    38. // emotion:{
    39. // emotions: '😀 😃 😄 😁 😆 😅 😂 🤣 😊 😇 🙂 🙃 😉'.split(' ') // 数组
    40. // }
    41. // fontFamily: {
    42. // fontFamilyList: [
    43. // // 元素支持两种形式
    44. // // 1. 字符串;
    45. // // 2. { name: 'xxx', value: 'xxx' }
    46. //
    47. // '黑体',
    48. // '楷体',
    49. // { name: '仿宋', value: '仿宋' },
    50. // 'Arial',
    51. // 'Tahoma',
    52. // 'Verdana'
    53. // ]
    54. // },
    55. // 上传图片的配置
    56. uploadImage: {
    57. server: process.env.VUE_APP_BASE_API + '/common/editorUpload/hce',
    58. // 字段名称
    59. fieldName: 'file',
    60. // 单个文件的最大体积限制,默认为 2M
    61. maxFileSize: 4 * 1024 * 1024, // 1M
    62. // 最多可上传几个文件,默认为 100
    63. maxNumberOfFiles: 20,
    64. // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
    65. allowedFileTypes: ['image/*'],
    66. base64LimitSize: 75 * 1024, // 100kb 以下插入 base64
    67. // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
    68. meta: {
    69. type: 'img'
    70. },
    71. // 将 meta 拼接到 url 参数中,默认 false
    72. metaWithUrl: false,
    73. // 自定义增加 http header
    74. headers: {
    75. // Accept: 'text/x-json',
    76. Authorization: 'Bearer ' + getToken()
    77. },
    78. // 跨域是否传递 cookie ,默认为 false
    79. withCredentials: false,
    80. // 超时时间,默认为 10 秒
    81. timeout: 5 * 1000, // 5 秒
    82. onBeforeUpload(files) {
    83. return files // 返回哪些文件可以上传
    84. // return false 会阻止上传
    85. },
    86. // 上传进度的回调函数
    87. onProgress(progress) {
    88. // console.log("onProgress", progress);
    89. },
    90. // 单个文件上传成功之后
    91. onSuccess(file, res) {
    92. Message({
    93. message: '上传成功',
    94. type: 'success'
    95. })
    96. // console.log("onSuccess", file, res);
    97. },
    98. // 单个文件上传失败
    99. onFailed(file, res) {
    100. Message({
    101. message: res.msg ? res.msg : '文件上传失败,可能是网络原因',
    102. type: 'error'
    103. })
    104. // console.log("onFailed", file, res);
    105. },
    106. // 上传错误,或者触发 timeout 超时
    107. onError(file, err, res) {
    108. Message({
    109. message: res.msg ? res.msg : '文件上传时发送错误,可能是网络原因,请检查!',
    110. type: 'error'
    111. })
    112. }
    113. // 用户自定义上传图片
    114. // customUpload(file, insertFn) {
    115. // const data = new FormData();
    116. // data.append("file", file); // file 即选中的文件
    117. // // data.append("type", 'img'); // file
    118. //
    119. // uploadFileHce(data).then(res => {
    120. // // 这个返回的是相对路径
    121. // let url = res.fileName;
    122. // // baseapi进行拼接
    123. // url = process.env.VUE_APP_BASE_API + url;
    124. // insertFn(url, '', res.fileName); //插入图片
    125. // })
    126. // }
    127. },
    128. // 上传视频的配置
    129. uploadVideo: {
    130. server: process.env.VUE_APP_BASE_API + '/common/editorUpload/hce',
    131. fieldName: 'file',
    132. // 单个文件的最大体积限制,默认为 10M, 现在是20M
    133. maxFileSize: 200 * 1024 * 1024, // 5M
    134. // 最多可上传几个文件,默认为 5
    135. maxNumberOfFiles: 3,
    136. // 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
    137. allowedFileTypes: ['video/*'],
    138. // 将 meta 拼接到 url 参数中,默认 false
    139. metaWithUrl: false,
    140. meta: {
    141. type: 'video'
    142. },
    143. // // 自定义增加 http header
    144. headers: {
    145. Authorization: 'Bearer ' + getToken()
    146. },
    147. // // 跨域是否传递 cookie ,默认为 false
    148. withCredentials: false,
    149. // // 超时时间,默认为 30 秒
    150. timeout: 21 * 1000, // 15 秒
    151. // 上传之前触发
    152. onBeforeUpload(file) {
    153. return file
    154. },
    155. // 上传进度的回调函数
    156. onProgress(progress) {
    157. // console.log("onProgress", progress);
    158. },
    159. // 单个文件上传成功之后
    160. onSuccess(file, res) {
    161. Message({
    162. message: '上传成功',
    163. type: 'success'
    164. })
    165. // console.log("onSuccess", file, res);
    166. },
    167. // 单个文件上传失败
    168. onFailed(file, res) {
    169. Message({
    170. message: res.msg ? res.msg : '文件上传失败,可能是网络原因',
    171. type: 'error'
    172. })
    173. // console.log("onFailed", file, res);
    174. },
    175. // 上传错误,或者触发 timeout 超时
    176. onError(file, err, res) {
    177. Message({
    178. message: res.msg ? res.msg : '文件上传时发送错误,可能是网络原因,请检查!',
    179. type: 'error'
    180. })
    181. }
    182. // 用户自定义上传视频
    183. // customUpload(file, insertFn) {
    184. // const data = new FormData();
    185. // data.append("file", file); // file 即选中的文件
    186. // data.append("type", 'video'); // file 即选中的文件
    187. //
    188. // uploadFileHce(data).then(res => {
    189. // // 这个返回的是相对路径
    190. // let url = res.fileName;
    191. // // baseapi进行拼接
    192. // url = process.env.VUE_APP_BASE_API + url;
    193. // insertFn(url, '', res.fileName); //插入图片
    194. // })
    195. // }
    196. }
    197. }
    198. }

    第三步,创建公共配置elementuiConf.js,依据vue引入路径创建

    1. // elementui配置
    2. // 日期选择配置
    3. import QRCode from "qrcodejs2";
    4. export const pickerOptions = {
    5. shortcuts: [{
    6. text: '未来一周',
    7. onClick(picker) {
    8. let start = getNowDate_01();
    9. let end = getNowDate_01();
    10. end.setTime(end.getTime() + 3600 * 1000 * 24 * 7);
    11. picker.$emit('pick', [start, end]);
    12. }
    13. }, {
    14. text: '未来一个月',
    15. onClick(picker) {
    16. let start = getNowDate_01();
    17. let end = getNowDate_01();
    18. end.setTime(end.getTime() + 3600 * 1000 * 24 * 30);
    19. picker.$emit('pick', [start, end]);
    20. }
    21. }, {
    22. text: '未来三个月',
    23. onClick(picker) {
    24. let start = getNowDate_01();
    25. let end = getNowDate_01();
    26. end.setTime(end.getTime() + 3600 * 1000 * 24 * 90);
    27. picker.$emit('pick', [start, end]);
    28. }
    29. }, {
    30. text: '未来半年',
    31. onClick(picker) {
    32. let start = getNowDate_01();
    33. let end = getNowDate_01();
    34. end.setTime(end.getTime() + 3600 * 1000 * 24 * 180);
    35. picker.$emit('pick', [start, end]);
    36. }
    37. }, {
    38. text: '未来一年',
    39. onClick(picker) {
    40. let start = getNowDate_01();
    41. let end = getNowDate_01();
    42. end.setTime(end.getTime() + 3600 * 1000 * 24 * 365);
    43. picker.$emit('pick', [start, end]);
    44. }
    45. }, {
    46. text: '未来两年',
    47. onClick(picker) {
    48. let start = getNowDate_01();
    49. let end = getNowDate_01();
    50. end.setTime(end.getTime() + 3600 * 1000 * 24 * 365 * 2);
    51. picker.$emit('pick', [start, end]);
    52. }
    53. }, {
    54. text: '未来五年',
    55. onClick(picker) {
    56. let start = getNowDate_01();
    57. let end = getNowDate_01();
    58. end.setTime(end.getTime() + 3600 * 1000 * 24 * 365 * 5);
    59. picker.$emit('pick', [start, end]);
    60. }
    61. }],
    62. //禁用当前日期之前的日期
    63. // disabledDate(time) {
    64. // //Date.now()是javascript中的内置函数,它返回自1970年1月1日00:00:00 UTC以来经过的毫秒数。
    65. // return time.getTime() < Date.now() - 8.64e7;
    66. // }
    67. }
    68. // 打开网页预览
    69. export function openIframe(url, show, urlItem) {
    70. // 判断是否需要打开新的ifrema还是在本页面打开
    71. if ((url.indexOf("app.urumqimtr.com") > -1) || (url.indexOf("localhost:") > -1)) {
    72. show.html = true;
    73. urlItem.htmlUrl = url;
    74. } else {
    75. window.open(url);
    76. }
    77. }
    78. // 获取图片地址,有的图片是网络图片。有的图片是本地图片,尽量都能展示
    79. export function imgUrlDeal(url) {
    80. if (url) {
    81. // 以/为开头说明是本地服务器图片
    82. if (url.startsWith("/")) {
    83. return process.env.VUE_APP_BASE_API + url
    84. }
    85. return url;
    86. } else {
    87. return null;
    88. }
    89. }
    90. // 格式化日年月日
    91. export function getNowDate_01() {
    92. let date = new Date();
    93. let year = date.getFullYear() // 年
    94. let month = date.getMonth() + 1; // 月
    95. let day = date.getDate(); // 日
    96. // 给一位数的数据前面加 “0”
    97. if (month >= 1 && month <= 9) {
    98. month = "0" + month;
    99. }
    100. if (day >= 0 && day <= 9) {
    101. day = "0" + day;
    102. }
    103. let time = year + "-" + month + "-" + day;
    104. time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm),'');
    105. date = new Date(time);
    106. return date;
    107. }
    108. // 格式化日对象
    109. export function getNowDate() {
    110. let date = new Date();
    111. let sign2 = ":";
    112. let year = date.getFullYear() // 年
    113. let month = date.getMonth() + 1; // 月
    114. let day = date.getDate(); // 日
    115. let hour = date.getHours(); // 时
    116. let minutes = date.getMinutes(); // 分
    117. let seconds = date.getSeconds() //秒
    118. const weekArr = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期天'];
    119. const week = weekArr[date.getDay()];
    120. // 给一位数的数据前面加 “0”
    121. if (month >= 1 && month <= 9) {
    122. month = "0" + month;
    123. }
    124. if (day >= 0 && day <= 9) {
    125. day = "0" + day;
    126. }
    127. if (hour >= 0 && hour <= 9) {
    128. hour = "0" + hour;
    129. }
    130. if (minutes >= 0 && minutes <= 9) {
    131. minutes = "0" + minutes;
    132. }
    133. if (seconds >= 0 && seconds <= 9) {
    134. seconds = "0" + seconds;
    135. }
    136. return year + "-" + month + "-" + day + " " + hour + sign2 + minutes + sign2 + seconds;
    137. }

    后台代码

    先创建模板文件

    1. <html lang="zh-CN">
    2. <head>
    3. <meta content="text/html; charset=utf-8" http-equiv="content-type">
    4. <meta name="referrer" content="never">
    5. <meta name="viewport"
    6. content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1,user-scalable=no">
    7. <style>
    8. html, body {
    9. padding: 0;
    10. margin: 0;
    11. }
    12. * {
    13. word-break:break-all;
    14. }
    15. #content {
    16. width: 92.2%;
    17. /*height: 100%;*/
    18. padding: 0;
    19. margin: 5px auto 0;
    20. }
    21. /* table 样式 */
    22. table {
    23. border-top: 1px solid #ccc;
    24. border-left: 1px solid #ccc;
    25. }
    26. table td,
    27. table th {
    28. border-bottom: 1px solid #ccc;
    29. border-right: 1px solid #ccc;
    30. padding: 3px 5px;
    31. }
    32. table th {
    33. border-bottom: 2px solid #ccc;
    34. text-align: center;
    35. }
    36. /* ul ol 样式 */
    37. ul, ol {
    38. margin: 10px 0 10px 20px;
    39. }
    40. /* video 设置*/
    41. video {
    42. width: 100%;
    43. height: auto;
    44. }
    45. style>
    46. <title>${title}title>
    47. head>
    48. <body>
    49. <div id="content">
    50. <#-- <h1 style="margin: 10px 0px 0px; padding: 0px; text-align: center; color: rgb(0, 0, 0); line-height: 30px; font-size: 21px; font-weight: normal; white-space: normal;">-->
    51. <#-- -->
    52. <#-- h1>-->
    53. <#-- <p style="text-align: center;"><span style="text-align: center; color: rgb(102, 102, 102); font-family: Courier New, Courier, monospace; font-size: 12px; white-space: normal;">2022-07-22span>p>-->
    54. <#-- 富文本中的内容 -->
    55. ${content}
    56. div>
    57. <script>
    58. window.onload = function () {
    59. //视频:
    60. let videos = document.getElementsByTagName('video');
    61. videos = Array.from(videos);
    62. videos.forEach((video) => {
    63. video.setAttribute("controls", "controls");
    64. video.setAttribute("controlslist", "nodownload");
    65. });
    66. // let imgs = document.getElementsByTagName('img');
    67. // imgs = Array.from(imgs);
    68. // imgs.forEach((img) => {
    69. // console.log(img)
    70. // img.style.width = '100%';
    71. // img.style.maxWidth = '100%';
    72. // img.style.height = 'auto';
    73. // });
    74. }
    75. // document.getElementsByTagName()
    76. script>
    77. body>
    78. html>

    这些配置是必要的,如果使用的是ruoyi那么请自行修改

    1. # 项目相关配置
    2. myWeb:
    3. # 名称
    4. name: myWeb
    5. # 版本
    6. version: 0.0.1
    7. # 版权年份
    8. copyrightYear: 2021
    9. # 实例演示开关
    10. demoEnabled: false
    11. # 文件路径 示例( Windows配置D:/myWeb/uploadPath,Linux配置 /home/myWeb/uploadPath)
    12. profile: ./uploadfile
    13. # profile: D:/home/file
    14. # 获取ip地址开关
    15. addressEnabled: false
    16. # 验证码类型 math 数组计算 char 字符验证
    17. captchaType: char
    18. # 共享盘映射地址,示例( Windows配置D:/myWeb/uploadPath,Linux配置 /home/myWeb/uploadPath)
    19. # sharedDisk: /HceShare/hceapp
    20. sharedDisk: D:/HceShare/hceapp
    21. # 上传的文件大小 140MB
    22. fileMaxSize: 146800640
    23. # 静态资源对应的服务器域名或地址
    24. # 生产地址
    25. #serverPrefix: https://xxxxxxx:9969/RecordFile
    26. # 测试环境地址
    27. #serverPrefix: http://xxxxxxx:8043/ggpt
    28. # 本地
    29. serverPrefix: http://localhost:8043/ggpt

    接下来最重要的地方,就是后台对富文本地址的处理,这个是控制器

    1. @Value("${serverPrefix}")
    2. public String serverPrefix;
    3. @RepeatSubmit(interval = 3500)
    4. public AjaxResult add(OpeNewsInfo opeNewsInfo) throws IOException, TemplateException, ParseException {
    5. LoginUser loginUser = getLoginUser();
    6. // 上传到共享盘,成功后会返回路径,调用的是otherAddressesUpload()
    7. String picaddr = FileUploadUtils.upload(myWeb.getSharedDiskImgPash(), myWeb.getSharedDisk(), opeNewsInfo.getPicNmFile());
    8. // 如果是本地页面才会生成页面
    9. if ("0".equals(opeNewsInfo.getPageType()) ) {
    10. // 获取内容
    11. String htmlAddress = FreemarkBeanUtil.getHtmlAddress(opeNewsInfo.getNewsTitle(), opeNewsInfo.getNewsContent(), loginUser);
    12. opeNewsInfo.setNewsLinkAddress(StringUtils.isNotEmpty(htmlAddress) ? serverPrefix + htmlAddress : "");
    13. }
    14. 插入数据库。。。。。
    15. }

    接下来必要的工具里类FileUploadUtils

    1. package com.goldsign.common.utils.file;
    2. import java.io.*;
    3. import java.lang.reflect.Method;
    4. import java.nio.charset.StandardCharsets;
    5. import java.util.Map;
    6. import cn.hutool.core.util.ByteUtil;
    7. import cn.hutool.core.util.URLUtil;
    8. import com.goldsign.common.utils.ServletUtils;
    9. import com.goldsign.common.utils.VideoUtil;
    10. import com.goldsign.common.utils.ip.IpUtils;
    11. import org.apache.commons.compress.utils.IOUtils;
    12. import org.apache.commons.fileupload.FileItem;
    13. import org.apache.commons.fileupload.disk.DiskFileItem;
    14. import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    15. import org.apache.commons.io.FilenameUtils;
    16. import org.apache.http.entity.ContentType;
    17. import org.jsoup.nodes.Element;
    18. import org.jsoup.select.Elements;
    19. import org.slf4j.Logger;
    20. import org.slf4j.LoggerFactory;
    21. import org.springframework.beans.factory.annotation.Autowired;
    22. import org.springframework.beans.factory.annotation.Value;
    23. import org.springframework.http.MediaType;
    24. import org.springframework.mock.web.MockMultipartFile;
    25. import org.springframework.stereotype.Component;
    26. import org.springframework.web.multipart.MultipartFile;
    27. import com.goldsign.common.config.GoldSignConfig;
    28. import com.goldsign.common.constant.Constants;
    29. import com.goldsign.common.exception.file.FileNameLengthLimitExceededException;
    30. import com.goldsign.common.exception.file.FileSizeLimitExceededException;
    31. import com.goldsign.common.exception.file.InvalidExtensionException;
    32. import com.goldsign.common.utils.DateUtils;
    33. import com.goldsign.common.utils.StringUtils;
    34. import com.goldsign.common.utils.uuid.IdUtils;
    35. import org.jsoup.Jsoup;
    36. import org.jsoup.nodes.Document;
    37. import org.springframework.web.multipart.commons.CommonsMultipartFile;
    38. import javax.annotation.PostConstruct;
    39. import javax.servlet.http.HttpServletRequest;
    40. /**
    41. * 文件上传工具类
    42. *
    43. * @author ruoyi
    44. */
    45. @Component
    46. public class FileUploadUtils {
    47. protected static final Logger log = LoggerFactory.getLogger(FileUploadUtils.class);
    48. @Autowired
    49. static IpUtils ipUtils;
    50. @Value("${serverPrefix}")
    51. public String serverPrefix;
    52. public static String serve_prefix;
    53. //利用@PostConstruct将yml中配置的值赋给本地的变量
    54. @PostConstruct
    55. public void getEnvironment() {
    56. this.serve_prefix = this.serverPrefix;
    57. }
    58. /**
    59. * 默认的文件名最大长度 100
    60. */
    61. public static final int DEFAULT_FILE_NAME_LENGTH = 350;
    62. /**
    63. * 默认上传的地址
    64. */
    65. private static String defaultBaseDir = GoldSignConfig.getProfile();
    66. public static void setDefaultBaseDir(String defaultBaseDir) {
    67. FileUploadUtils.defaultBaseDir = defaultBaseDir;
    68. }
    69. public static String getDefaultBaseDir() {
    70. return defaultBaseDir;
    71. }
    72. /**
    73. * 以默认配置进行文件上传
    74. *
    75. * @param file 上传的文件
    76. * @return 文件名称
    77. * @throws Exception
    78. */
    79. public static final String upload(MultipartFile file) throws IOException {
    80. try {
    81. return upload(getDefaultBaseDir(), null, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
    82. } catch (Exception e) {
    83. throw new IOException(e.getMessage(), e);
    84. }
    85. }
    86. /**
    87. * 根据文件路径上传
    88. *
    89. * @param baseDir 相对应用的基目录
    90. * @param fileHttpMapping 文件所存在的映射根目录,该目录被资源拦截器映射成了本服务的的静态资源
    91. * @param file 上传的文件
    92. * @return 文件名称
    93. * @throws IOException
    94. */
    95. public static final String upload(String baseDir, String fileHttpMapping, MultipartFile file) throws IOException {
    96. try {
    97. return upload(baseDir, fileHttpMapping, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
    98. } catch (Exception e) {
    99. throw new IOException(e.getMessage(), e);
    100. }
    101. }
    102. /**
    103. * 文件上传
    104. *
    105. * @param baseDir 相对应用的基目录
    106. * @param fileHttpMapping 文件所存在的映射根目录,该目录被资源拦截器映射成了本服务的的静态资源
    107. * @param file 上传的文件
    108. * @param allowedExtension 上传文件类型
    109. * @return 返回上传成功的文件名
    110. * @throws FileSizeLimitExceededException 如果超出最大大小
    111. * @throws FileNameLengthLimitExceededException 文件名太长
    112. * @throws IOException 比如读写文件出错时
    113. * @throws InvalidExtensionException 文件校验异常
    114. */
    115. public static final String upload(String baseDir, String fileHttpMapping, MultipartFile file, String[] allowedExtension) throws Exception {
    116. int fileNamelength = file.getOriginalFilename().length();
    117. if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
    118. throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
    119. }
    120. assertAllowed(file, allowedExtension);
    121. String fileName = extractFilename(file);
    122. File desc = getAbsoluteFile(baseDir, fileName);
    123. file.transferTo(desc);
    124. String pathFileName = getPathFileName(baseDir, fileHttpMapping, fileName);
    125. return pathFileName;
    126. }
    127. /**
    128. * 编码文件名
    129. */
    130. public static final String extractFilename(MultipartFile file) {
    131. String fileName = file.getOriginalFilename();
    132. String extension = getExtension(file);
    133. fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
    134. return fileName;
    135. }
    136. public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
    137. File desc = new File(uploadDir + File.separator + fileName);
    138. if (!desc.exists()) {
    139. if (!desc.getParentFile().exists()) {
    140. desc.getParentFile().mkdirs();
    141. }
    142. }
    143. return desc;
    144. }
    145. public static final String getPathFileName(String uploadDir, String fileHttpMapping, String fileName) throws IOException {
    146. if (fileHttpMapping == null) {
    147. fileHttpMapping = GoldSignConfig.getProfile();
    148. }
    149. String pathFileName;
    150. int dirLastIndex = fileHttpMapping.length() + 1;
    151. String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
    152. // 如果是本服務對應的文件夾,那麽就加上指定前綴,可以让该请求访问本服务时,映射到指定的静态资源上
    153. if (fileHttpMapping.equals(GoldSignConfig.getProfile())) {
    154. // Constants.RESOURCE_PREFIX 是本服务默认的开头前缀
    155. pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
    156. } else {
    157. // 这个就不用加前缀, 因为其他服务(hce后管)也要访问,所以我们文件路径的格式不能和框架指定的格式保持一致,要跟hce后管的文件系统保持一致,而且上传的时候我们也指定了前缀,如果要加新的文件类型,则在配置文件中增加配置,同时在ResourcesConfig中增加对该目录的映射处理
    158. pathFileName = "/" + currentDir + "/" + fileName;
    159. }
    160. return pathFileName;
    161. }
    162. /**
    163. * 文件大小校验
    164. *
    165. * @param file 上传的文件
    166. * @return
    167. * @throws FileSizeLimitExceededException 如果超出最大大小
    168. * @throws InvalidExtensionException
    169. */
    170. public static final void assertAllowed(MultipartFile file, String[] allowedExtension) throws FileSizeLimitExceededException, InvalidExtensionException {
    171. long size = file.getSize();
    172. long fileMaxSize = GoldSignConfig.getFileMaxSize();
    173. if (fileMaxSize != -1 && size > fileMaxSize) {
    174. throw new FileSizeLimitExceededException(fileMaxSize / 1024 / 1024);
    175. }
    176. String fileName = file.getOriginalFilename();
    177. String extension = getExtension(file);
    178. if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
    179. if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
    180. throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
    181. fileName);
    182. } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {
    183. throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
    184. fileName);
    185. } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {
    186. throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
    187. fileName);
    188. } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) {
    189. throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
    190. fileName);
    191. } else {
    192. throw new InvalidExtensionException(allowedExtension, extension, fileName);
    193. }
    194. }
    195. }
    196. /**
    197. * 判断MIME类型是否是允许的MIME类型
    198. *
    199. * @param extension
    200. * @param allowedExtension
    201. * @return
    202. */
    203. public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
    204. for (String str : allowedExtension) {
    205. if (str.equalsIgnoreCase(extension)) {
    206. return true;
    207. }
    208. }
    209. return false;
    210. }
    211. // 根据网页内容生成h5文件
    212. public static String getH5Path(String h5Path, String content) throws IOException {
    213. InputStream inputStream = null;
    214. try {
    215. inputStream = new ByteArrayInputStream(content.getBytes());
    216. MultipartFile vFile = new MockMultipartFile("xx.html", "xx.html", ContentType.TEXT_HTML.toString(), inputStream);
    217. // 保存新闻,这里获取的是h5
    218. h5Path = upload(GoldSignConfig.getSharedDiskH5Pash(), GoldSignConfig.getSharedDisk(), vFile);
    219. } catch (IOException e) {
    220. e.printStackTrace();
    221. log.error("网页生成上传失败,网页URL:{},异常:{}", h5Path, e.getMessage());
    222. } finally {
    223. if (inputStream != null) {
    224. inputStream.close();
    225. }
    226. }
    227. return h5Path;
    228. }
    229. /**
    230. * 获取文件名的后缀
    231. *
    232. * @param file 表单文件
    233. * @return 后缀名
    234. */
    235. public static final String getExtension(MultipartFile file) {
    236. String extension = FilenameUtils.getExtension(file.getOriginalFilename());
    237. if (StringUtils.isEmpty(extension)) {
    238. extension = MimeTypeUtils.getExtension(file.getContentType());
    239. }
    240. return extension;
    241. }
    242. /**
    243. * 将相对服务的相对路径,修改为相对为本文件的路径,避免服务地址修改的时候,或者服务名修改的时候产生不必要的维护
    244. *
    245. * @param els
    246. */
    247. public static void extracted(Elements els) throws IOException {
    248. String src;
    249. for (Element el : els) {
    250. if ("video".equals(el.tag().getName())) {
    251. Elements source = el.select("source[src]");
    252. src = source.attr("src");
    253. if (StringUtils.isNotBlank(src)) {
    254. // 第一步,判断是否是绝对路径
    255. if (!isAbsolutePath(src)) {
    256. // 相对路径设置
    257. el.attr("src", getHtmlTagRelativeUrl(src));
    258. }
    259. // 第二步,先不修改src的地址为相对地址,先获取视频封面字节数据
    260. byte[] bytesImg = VideoUtil.getScreenshot(src);
    261. if (bytesImg != null) {
    262. MultipartFile vFile = null;
    263. InputStream inputStream = null;
    264. try {
    265. // 构造inputStream
    266. inputStream = new ByteArrayInputStream(bytesImg);
    267. // 将inputStream转成MultipartFile
    268. vFile = new MockMultipartFile("xx.jpeg", "xx.jpeg", ContentType.IMAGE_JPEG.toString(), inputStream);
    269. // 上传封面。获取上传后的地址
    270. String h5Path = upload(GoldSignConfig.getSharedDiskCVideoCoverPath(), GoldSignConfig.getSharedDisk(), vFile);
    271. el.attr("poster", getHtmlTagRelativeUrl(h5Path));
    272. } catch (IOException e) {
    273. e.printStackTrace();
    274. log.error("视频封面转换失败,视频URL:{},异常:{}", src, e.getMessage());
    275. } finally {
    276. if (inputStream != null) {
    277. inputStream.close();
    278. }
    279. }
    280. }
    281. }
    282. } else {
    283. src = el.attr("src");
    284. el.attr("src", getHtmlTagRelativeUrl(src));
    285. String style = el.attr("style");
    286. el.attr("style", "max-width: 100%;height: auto");
    287. }
    288. }
    289. }
    290. /**
    291. * 为标签设置图片,修改相对路径
    292. *
    293. * @param src
    294. */
    295. private static String getHtmlTagRelativeUrl(String src) {
    296. // 判断如果有 配置文件中的serverPrefix地址,那么就替换成空字符串,这样在生成的文件中保存的就是相对地址了,相对地址不依赖域名,更灵活
    297. src = src.replace(serve_prefix, "");
    298. // 如果是以userfile开头的就替换
    299. if (src.startsWith("/userfiles/ggpt/")) {
    300. // 这里替换成相对路径,避免不必要的拼接数据,造成元数据污染
    301. return src.replace("/userfiles/ggpt/", "../../../../");
    302. }
    303. return src;
    304. }
    305. /**
    306. * 将字符串通过outputStream输入到file中
    307. * 这个方法有问题,会在本地生成文件,所以放弃使用了
    308. *
    309. * @param title
    310. * @param htmlText
    311. * @return
    312. * @throws IOException
    313. */
    314. public static CommonsMultipartFile getCommonsMultipartFile(String title, String htmlText, File file) throws IOException {
    315. // file为空就使用htmlcontent,否则就使用传递进来的file
    316. if (file == null) {
    317. file = new File(title + ".html");
    318. OutputStream fos = new FileOutputStream(file);
    319. fos.write(htmlText.getBytes(StandardCharsets.UTF_8));
    320. try {
    321. fos.flush();
    322. fos.close();
    323. } catch (IOException e) {
    324. }
    325. }
    326. DiskFileItem fileItem = (DiskFileItem) new DiskFileItemFactory().createItem("file", MediaType.ALL_VALUE, true, file.getName());
    327. try (InputStream input = new FileInputStream(file); OutputStream os = fileItem.getOutputStream()) {
    328. IOUtils.copy(input, os);
    329. } catch (Exception e) {
    330. throw new IllegalArgumentException("Invalid file: " + e, e);
    331. }
    332. CommonsMultipartFile multipartFile = new CommonsMultipartFile(fileItem);
    333. return multipartFile;
    334. }
    335. /**
    336. * 判断是否绝对路径
    337. * 当路径以 / 开头则为相对路径,否则视为绝对路径
    338. *
    339. * @param uploadDir
    340. * @return
    341. */
    342. private static boolean isAbsolutePath(String uploadDir) {
    343. if (uploadDir.startsWith("/")) {
    344. return false;
    345. }
    346. return true;
    347. }
    348. /**
    349. * 获取本机地址
    350. *
    351. * @return
    352. */
    353. public static String getUrl() {
    354. return getDomain(ServletUtils.getRequest());
    355. }
    356. public static String getDomain(HttpServletRequest request) {
    357. StringBuffer url = request.getRequestURL();
    358. String contextPath = request.getServletContext().getContextPath();
    359. return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
    360. }
    361. }

    视频封面截取工具VideoUtil

    1. package com.goldsign.common.utils;
    2. import cn.hutool.core.img.ImgUtil;
    3. import com.goldsign.common.exception.ServiceException;
    4. import com.goldsign.common.utils.ip.IpUtils;
    5. import com.goldsign.common.utils.uuid.IdUtils;
    6. //import org.bytedeco.javacpp.opencv_core;
    7. //import org.bytedeco.javacv.FFmpegFrameGrabber;
    8. //import org.bytedeco.javacv.Frame;
    9. //import org.bytedeco.javacv.Java2DFrameConverter;
    10. //import java.awt.*;
    11. //import java.awt.image.BufferedImage;
    12. import java.awt.*;
    13. import java.awt.image.BufferedImage;
    14. import java.io.ByteArrayOutputStream;
    15. import java.io.File;
    16. import java.io.IOException;
    17. //import java.io.File;
    18. //
    19. import org.bytedeco.javacpp.opencv_core;
    20. import org.bytedeco.javacv.*;
    21. import org.bytedeco.javacv.Frame;
    22. import org.slf4j.Logger;
    23. import org.slf4j.LoggerFactory;
    24. import org.springframework.beans.factory.annotation.Autowired;
    25. import org.springframework.beans.factory.annotation.Value;
    26. import org.springframework.stereotype.Component;
    27. import javax.annotation.PostConstruct;
    28. import javax.imageio.ImageIO;
    29. /**
    30. * @author:yuchen
    31. * @createTime:2022/7/11 16:07
    32. */
    33. @Component
    34. public class VideoUtil {
    35. protected static final Logger log = LoggerFactory.getLogger(VideoUtil.class);
    36. public static void main(String[] args) {
    37. // byte[] screenshot = getScreenshot("C:\\Users\\86181\\Downloads\\下载 (1).mp4");
    38. // System.out.println(file.getName());
    39. // getVideoPic();
    40. }
    41. @Autowired
    42. public IpUtils ipUtils;
    43. @Autowired
    44. public static IpUtils _ipUtils;
    45. @Value("${serverPrefix}")
    46. public String serverPrefix;
    47. public static String serve_prefix;
    48. //利用@PostConstruct将yml中配置的值赋给本地的变量
    49. @PostConstruct
    50. public void getEnvironment(){
    51. this.serve_prefix = this.serverPrefix;
    52. this._ipUtils = this.ipUtils;
    53. }
    54. // public static String getVideoPic() {
    55. // String url = "https://xxx/xxxx.mp4";
    56. // String value = "1111123";
    57. // String dir = value +"/";
    58. // String fileName = FileUtil.getName(url);// 100.mp4
    59. // String targetFileName = "thumb_"+fileName.substring(0, fileName.lastIndexOf("."))+".jpg";
    60. // File filePath = new File(PATH+dir);
    61. // if (!filePath.exists()) {
    62. // boolean mkdirs = filePath.mkdirs();
    63. // if(mkdirs){
    64. // log.info("文件夹创建成功:{}",filePath.getAbsolutePath());
    65. // }
    66. // }
    67. // String outPath = PATH+dir+targetFileName;
    68. // File outFile = getScreenshot(url, outPath);
    69. // if(outFile != null){
    70. // String fileName1 = AwsFileUtil.uploadS3Us(outFile, imei, targetFileName);
    71. // System.out.println(fileName1);
    72. // boolean delete = outFile.delete();
    73. // if(delete){
    74. // log.info("删除本地临时文件成功");
    75. // }
    76. // return fileName1;
    77. // }
    78. // return "";
    79. // }
    80. /**
    81. * 获取视频封面图
    82. *
    83. * @param filePath 视频地址
    84. * @return map
    85. */
    86. // public static byte[] getScreenshot(String filePath) {
    87. // log.info("截取视频截图开始:Video={}", filePath);
    88. // filePath = filePath.replaceAll("\\\\", "/");
    89. // File file = null;
    90. // FFmpegFrameGrabber grabber = null;
    91. // try {
    92. // grabber = FFmpegFrameGrabber.createDefault(filePath);
    93. // grabber.setOption("stimeout", "2000000");
    94. // grabber.start();
    95. // //设置视频截取帧(建议从5帧开始,防止全是黑屏)
    96. // Frame frame = null;
    97. // for (int j = 0; j < 5; j++) {
    98. // frame = grabber.grabImage();
    99. // }
    100. // //视频旋转度
    101. // String rotate = grabber.getVideoMetadata("rotate");
    102. // Java2DFrameConverter converter = new Java2DFrameConverter();
    103. // //绘制图片
    104. // BufferedImage image = converter.getBufferedImage(frame);
    105. // if (rotate != null) {
    106. // // 旋转图片
    107. // image = rotate(image, Integer.parseInt(rotate));
    108. // }
    109. // //创建文件
    110. file = new File(IdUtils.randomUUID() + ".jpg");
    111. ImgUtil.write(image, file);
    112. //
    113. // ByteArrayOutputStream out = new ByteArrayOutputStream();
    114. // boolean flag = ImageIO.write(image, "gif", out);
    115. //
    116. // byte[] b = out.toByteArray();
    117. // out.write(b);
    118. // log.info("截取视频截图成功");
    119. // return b;
    120. // } catch (Exception e) {
    121. // throw new ServiceException("获取视频封面图,Error,请检查视频链接是否正常");
    122. // } finally {
    123. // if (grabber != null) {
    124. // try {
    125. // grabber.stop();
    126. // } catch (Exception ignored) {
    127. // }
    128. // }
    129. // }
    130. // }
    131. public static byte[] getScreenshot(String filePath) {
    132. // 说明是外部资源,需要拼接上https
    133. if (filePath.startsWith("//") && !filePath.startsWith("/userfiles/ggpt")){
    134. filePath = "https:" + filePath;
    135. }
    136. // 有些连文件地址不太正常,且没有后缀,不要紧; 文档资料参考链接:【https://www.codenong.com/7273573/】
    137. // 如果发现是以yml配置开头的url路径,那么就替换成localhost路径
    138. filePath = filePath.replaceFirst(serve_prefix,_ipUtils.getAccessPath());
    139. byte[] bytes = null;
    140. FFmpegFrameGrabber ff = null;
    141. try {
    142. ff = FFmpegFrameGrabber.createDefault(filePath);
    143. ff.start();
    144. String rotate = ff.getVideoMetadata("rotate");
    145. Frame f;
    146. int i = 0;
    147. while (i < 6) {
    148. f = ff.grabImage();
    149. opencv_core.IplImage src = null;
    150. if (null != rotate && rotate.length() > 1) {
    151. OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();
    152. src = converter.convert(f);
    153. f = converter.convert(rotate(src, Integer.valueOf(rotate)));
    154. }
    155. i++;
    156. if (i == 6) {
    157. bytes = doExecuteFrame(f);
    158. }
    159. }
    160. ff.stop();
    161. } catch (FrameGrabber.Exception e) {
    162. log.error("视频截取失败");
    163. // throw new ServiceException("获取视频封面图,Error,请检查视频链接是否正常");
    164. } finally {
    165. if (ff != null) {
    166. try {
    167. ff.close();
    168. } catch (Exception e) {
    169. }
    170. }
    171. }
    172. return bytes;
    173. }
    174. /*
    175. * 旋转角度的
    176. */
    177. public static opencv_core.IplImage rotate(opencv_core.IplImage src, int angle) {
    178. opencv_core.IplImage img = opencv_core.IplImage.create(src.height(), src.width(), src.depth(), src.nChannels());
    179. opencv_core.cvTranspose(src, img);
    180. opencv_core.cvFlip(img, img, angle);
    181. return img;
    182. }
    183. public static byte[] doExecuteFrame(Frame f) {
    184. if (null == f || null == f.image) {
    185. return new byte[0];
    186. }
    187. Java2DFrameConverter converter = new Java2DFrameConverter();
    188. String imageMat = "jpg";
    189. BufferedImage bi = converter.getBufferedImage(f);
    190. System.out.println("width:" + bi.getWidth());
    191. System.out.println("height:" + bi.getHeight());
    192. ByteArrayOutputStream out = new ByteArrayOutputStream();
    193. try {
    194. boolean flag = ImageIO.write(bi, "jpg", out);
    195. byte[] b = out.toByteArray();
    196. log.info("截取视频截图成功");
    197. return b;
    198. } catch (IOException e) {
    199. log.error("封面截取失败");
    200. } finally {
    201. if (out != null) {
    202. try {
    203. out.close();
    204. } catch (IOException e) {
    205. }
    206. }
    207. }
    208. return null;
    209. }
    210. /**
    211. * @param src ""
    212. * @param angel 视频旋转度
    213. * @return BufferedImage
    214. * @Description 根据视频旋转度来调整图片
    215. */
    216. private static BufferedImage rotate(BufferedImage src, int angel) {
    217. int src_width = src.getWidth(null);
    218. int src_height = src.getHeight(null);
    219. int type = src.getColorModel().getTransparency();
    220. Rectangle rect_des = calcRotatedSize(new Rectangle(new Dimension(src_width, src_height)), angel);
    221. BufferedImage bi = new BufferedImage(rect_des.width, rect_des.height, type);
    222. Graphics2D g2 = bi.createGraphics();
    223. g2.translate((rect_des.width - src_width) / 2, (rect_des.height - src_height) / 2);
    224. g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2);
    225. g2.drawImage(src, 0, 0, null);
    226. g2.dispose();
    227. return bi;
    228. }
    229. /**
    230. * @param src ""
    231. * @param angel ""
    232. * @return Rectangle
    233. * @Description: 计算图片旋转大小
    234. */
    235. private static Rectangle calcRotatedSize(Rectangle src, int angel) {
    236. if (angel >= 90) {
    237. if (angel / 90 % 2 == 1) {
    238. int temp = src.height;
    239. src.height = src.width;
    240. src.width = temp;
    241. }
    242. angel = angel % 90;
    243. }
    244. double r = Math.sqrt(src.height * src.height + src.width * src.width) / 2;
    245. double len = 2 * Math.sin(Math.toRadians(angel) / 2) * r;
    246. double angel_alpha = (Math.PI - Math.toRadians(angel)) / 2;
    247. double angel_dalta_width = Math.atan((double) src.height / src.width);
    248. double angel_dalta_height = Math.atan((double) src.width / src.height);
    249. int len_dalta_width = (int) (len * Math.cos(Math.PI - angel_alpha - angel_dalta_width));
    250. int len_dalta_height = (int) (len * Math.cos(Math.PI - angel_alpha - angel_dalta_height));
    251. int des_width = src.width + len_dalta_width * 2;
    252. int des_height = src.height + len_dalta_height * 2;
    253. return new java.awt.Rectangle(new Dimension(des_width, des_height));
    254. }
    255. }

    对yml中的配置文件进行装配myWebnConfig

    1. package com.goldsign.common.config;
    2. import org.apache.commons.lang3.time.DateUtils;
    3. import org.springframework.boot.context.properties.ConfigurationProperties;
    4. import org.springframework.stereotype.Component;
    5. import java.io.File;
    6. import java.util.Date;
    7. /**
    8. * 读取项目相关配置
    9. *
    10. * @author ruoyi
    11. */
    12. @Component
    13. @ConfigurationProperties(prefix = "myWeb")
    14. public class myWebSignConfig {
    15. /**GoldSignConfig
    16. * 项目名称
    17. */
    18. private String name;
    19. /**
    20. * 版本
    21. */
    22. private String version;
    23. /**
    24. * 版权年份
    25. */
    26. private String copyrightYear;
    27. /**
    28. * 实例演示开关
    29. */
    30. private boolean demoEnabled;
    31. /**
    32. * 上传最大文件大小配置
    33. */
    34. private static long fileMaxSize;
    35. /**
    36. * 上传路径
    37. */
    38. private static String profile;
    39. /* 共享盘地址*/
    40. private static String sharedDisk;
    41. /**
    42. * 获取地址开关
    43. */
    44. private static boolean addressEnabled;
    45. public String getName() {
    46. return name;
    47. }
    48. public void setName(String name) {
    49. this.name = name;
    50. }
    51. public String getVersion() {
    52. return version;
    53. }
    54. public void setVersion(String version) {
    55. this.version = version;
    56. }
    57. public String getCopyrightYear() {
    58. return copyrightYear;
    59. }
    60. public void setCopyrightYear(String copyrightYear) {
    61. this.copyrightYear = copyrightYear;
    62. }
    63. public boolean isDemoEnabled() {
    64. return demoEnabled;
    65. }
    66. public void setDemoEnabled(boolean demoEnabled) {
    67. this.demoEnabled = demoEnabled;
    68. }
    69. public static String getProfile() {
    70. return profile;
    71. }
    72. public void setProfile(String profile) {
    73. GoldSignConfig.profile = profile;
    74. }
    75. public static boolean isAddressEnabled() {
    76. return addressEnabled;
    77. }
    78. public void setAddressEnabled(boolean addressEnabled) {
    79. GoldSignConfig.addressEnabled = addressEnabled;
    80. }
    81. public static String getSharedDisk() {
    82. return sharedDisk;
    83. }
    84. public void setSharedDisk(String sharedDisk) {
    85. GoldSignConfig.sharedDisk = sharedDisk;
    86. }
    87. public static long getFileMaxSize() {
    88. return fileMaxSize;
    89. }
    90. public void setFileMaxSize(long fileMaxSize) {
    91. GoldSignConfig.fileMaxSize = fileMaxSize;
    92. }
    93. /**
    94. * 获取导入上传路径
    95. */
    96. public static String getImportPath() {
    97. return getProfile() + "/import";
    98. }
    99. /**
    100. * 获取头像上传路径
    101. */
    102. public static String getAvatarPath() {
    103. return getProfile() + "/avatar";
    104. }
    105. /**
    106. * 获取下载路径
    107. */
    108. public static String getDownloadPath() {
    109. return getProfile() + "/download/";
    110. }
    111. /**
    112. * 获取上传路径
    113. */
    114. public static String getUploadPath() {
    115. return getProfile() + "/upload";
    116. }
    117. /**
    118. * 获取共享盘图片上传路径
    119. *
    120. * @return
    121. */
    122. public static String getSharedDiskImgPash() {
    123. // 如果上传的目录不1位则补0
    124. String path = getSharedDisk() + "/userfiles/ggpt/image";
    125. File file = new File(path);
    126. if (file.exists()) {
    127. file.mkdirs();
    128. }
    129. return path;
    130. }
    131. /**
    132. * 获取共享盘【视频文件】上传路径
    133. *
    134. * @return
    135. */
    136. public static String getSharedDiskVideoPash() {
    137. // 如果上传的目录不1位则补0
    138. String path = getSharedDisk() + "/userfiles/ggpt/video";
    139. File file = new File(path);
    140. if (file.exists()) {
    141. file.mkdirs();
    142. }
    143. return path;
    144. }
    145. /**
    146. * 获取共享盘网页上传路径
    147. *
    148. * @return
    149. */
    150. public static String getSharedDiskH5Pash() {
    151. // 如果上传的目录不1位则补0
    152. String path = getSharedDisk() + "/userfiles/ggpt/h5";
    153. File file = new File(path);
    154. if (file.exists()) {
    155. file.mkdirs();
    156. }
    157. return path;
    158. }
    159. /**
    160. * 获取共享盘图片上传路径
    161. *
    162. * @return
    163. */
    164. public static String getSharedDiskCVideoCoverPath() {
    165. // 如果上传的目录不1位则补0
    166. String path = getSharedDisk() + "/userfiles/ggpt/videoCover";
    167. File file = new File(path);
    168. if (file.exists()) {
    169. file.mkdirs();
    170. }
    171. return path;
    172. }
    173. }

    必须依赖,如果是ruoyi框架请加载common模块下

    1. <dependency>
    2. <groupId>org.jsoupgroupId>
    3. <artifactId>jsoupartifactId>
    4. <version>1.8.3version>
    5. dependency>
    6. 视频处理
    7. <dependency>
    8. <groupId>org.bytedecogroupId>
    9. <artifactId>javacv-platformartifactId>
    10. <version>1.3.1version>
    11. dependency>
    12. <dependency>
    13. <groupId>org.springframeworkgroupId>
    14. <artifactId>spring-testartifactId>
    15. dependency>
    16. <dependency>
    17. <groupId>cn.hutoolgroupId>
    18. <artifactId>hutool-allartifactId>
    19. <version>5.7.13version>
    20. dependency>

    最重要的环节来了,springmvc添加 addResourceHandlers

    1. @Override
    2. public void addResourceHandlers(ResourceHandlerRegistry registry) {
    3. /** 本地文件上传路径 */
    4. // 后管服务自带的目录
    5. registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**")
    6. // 映射到指定的目錄
    7. .addResourceLocations("file:" + GoldSignConfig.getProfile() + "/");
    8. // 这里是hce后管的文件目录,如果是userfiles路径,那么映射到D:/HceShare/hceapp + “/userfiles/” 目录下面
    9. registry.addResourceHandler("/userfiles" + "/**")
    10. // 映射到指定的目錄
    11. .addResourceLocations("file:" + GoldSignConfig.getSharedDisk() + "/userfiles/");
    12. /** swagger配置 */
    13. registry.addResourceHandler("/swagger-ui/**")
    14. .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
    15. }

    最后ruoyi的权限框架不要拦截我们的目录文件

    类OpeAdPicInfo

    1. package com.goldsign.domain;
    2. import com.baomidou.mybatisplus.annotation.IdType;
    3. import com.baomidou.mybatisplus.annotation.KeySequence;
    4. import com.baomidou.mybatisplus.annotation.TableId;
    5. import com.baomidou.mybatisplus.extension.activerecord.Model;
    6. import com.fasterxml.jackson.annotation.JsonFormat;
    7. import com.goldsign.common.annotation.Excel;
    8. import lombok.experimental.FieldDefaults;
    9. import org.apache.commons.lang.builder.ToStringBuilder;
    10. import org.apache.commons.lang.builder.ToStringStyle;
    11. import org.springframework.web.multipart.MultipartFile;
    12. import java.util.Date;
    13. /**
    14. * 广告轮播图对象 OPE_AD_PIC_INFO
    15. *
    16. * @author goldsign
    17. * @date 2022-07-08
    18. */
    19. @KeySequence(value = "SEQ_OPE_AD_PIC_INFO", clazz = String.class)
    20. public class OpeAdPicInfo extends Model {
    21. private static final long serialVersionUID = 1L;
    22. /** 上传的图片文件 */
    23. private MultipartFile picNmFile;
    24. /** $column.columnComment */
    25. @TableId(value = "id", type = IdType.INPUT)
    26. private String id;
    27. /** 图片名称 */
    28. @Excel(name = "图片名称")
    29. private String picNm;
    30. /** 图片地址 */
    31. @Excel(name = "图片地址")
    32. private String picAddr;
    33. /** 图片描叙 */
    34. @Excel(name = "图片描叙")
    35. private String picDesc;
    36. /** 图片是否显示 */
    37. @Excel(name = "图片是否显示")
    38. private String picShow;
    39. /** 创建人 */
    40. @Excel(name = "创建人")
    41. private String creater;
    42. /** 创建时间 */
    43. @Excel(name = "创建时间")
    44. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    45. private Date createDt;
    46. /** 最新修改人 */
    47. @Excel(name = "最新修改人")
    48. private String modifier;
    49. /** 最新修改时间 */
    50. @Excel(name = "最新修改时间")
    51. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    52. private Date modifyDt;
    53. /** 备注 */
    54. @Excel(name = "备注")
    55. private String remarks;
    56. /** 删除标记 */
    57. private String deleteFlag;
    58. /** 图片超链接 */
    59. @Excel(name = "图片超链接")
    60. private String picHyperlink;
    61. /** 广告类型 */
    62. @Excel(name = "广告类型")
    63. private String adType;
    64. /** 广告类型 */
    65. @Excel(name = "广告类型")
    66. private String linkType;
    67. /** 倒计时(秒) */
    68. @Excel(name = "倒计时", readConverterExp = "秒")
    69. private Integer countdown;
    70. /** $column.columnComment */
    71. @Excel(name = "倒计时", readConverterExp = "$column.readConverterExp()")
    72. private String savepic;
    73. @Excel(name = "发布时间")
    74. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    75. private Date releaseTime;
    76. @Excel(name = "锁定时间")
    77. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    78. private Date lockTime;
    79. /** 富文本 */
    80. private String richText;
    81. // 网页类型,0本地页面,1第三方页面
    82. private String pageType;
    83. public String getPageType() {
    84. return pageType;
    85. }
    86. public void setPageType(String pageType) {
    87. this.pageType = pageType;
    88. }
    89. public Date getReleaseTime() {
    90. return releaseTime;
    91. }
    92. public void setReleaseTime(Date releaseTime) {
    93. this.releaseTime = releaseTime;
    94. }
    95. public Date getLockTime() {
    96. return lockTime;
    97. }
    98. public void setLockTime(Date lockTime) {
    99. this.lockTime = lockTime;
    100. }
    101. public String getRichText() {
    102. return richText;
    103. }
    104. public void setRichText(String richText) {
    105. this.richText = richText;
    106. }
    107. public void setId(String id)
    108. {
    109. this.id = id;
    110. }
    111. public String getId()
    112. {
    113. return id;
    114. }
    115. public void setPicNm(String picNm)
    116. {
    117. this.picNm = picNm;
    118. }
    119. public String getPicNm()
    120. {
    121. return picNm;
    122. }
    123. public void setPicAddr(String picAddr)
    124. {
    125. this.picAddr = picAddr;
    126. }
    127. public String getPicAddr()
    128. {
    129. return picAddr;
    130. }
    131. public void setPicDesc(String picDesc)
    132. {
    133. this.picDesc = picDesc;
    134. }
    135. public String getPicDesc()
    136. {
    137. return picDesc;
    138. }
    139. public void setPicShow(String picShow)
    140. {
    141. this.picShow = picShow;
    142. }
    143. public String getPicShow()
    144. {
    145. return picShow;
    146. }
    147. public void setCreater(String creater)
    148. {
    149. this.creater = creater;
    150. }
    151. public String getCreater() {
    152. return creater;
    153. }
    154. public Date getCreateDt() {
    155. return createDt;
    156. }
    157. public void setCreateDt(Date createDt) {
    158. this.createDt = createDt;
    159. }
    160. public String getModifier() {
    161. return modifier;
    162. }
    163. public void setModifier(String modifier) {
    164. this.modifier = modifier;
    165. }
    166. public Date getModifyDt() {
    167. return modifyDt;
    168. }
    169. public void setModifyDt(Date modifyDt) {
    170. this.modifyDt = modifyDt;
    171. }
    172. public void setRemarks(String remarks) {
    173. this.remarks = remarks;
    174. }
    175. public String getRemarks()
    176. {
    177. return remarks;
    178. }
    179. public void setDeleteFlag(String deleteFlag)
    180. {
    181. this.deleteFlag = deleteFlag;
    182. }
    183. public String getDeleteFlag()
    184. {
    185. return deleteFlag;
    186. }
    187. public void setPicHyperlink(String picHyperlink)
    188. {
    189. this.picHyperlink = picHyperlink;
    190. }
    191. public String getPicHyperlink()
    192. {
    193. return picHyperlink;
    194. }
    195. public void setAdType(String adType)
    196. {
    197. this.adType = adType;
    198. }
    199. public String getAdType()
    200. {
    201. return adType;
    202. }
    203. public void setLinkType(String linkType)
    204. {
    205. this.linkType = linkType;
    206. }
    207. public String getLinkType()
    208. {
    209. return linkType;
    210. }
    211. public void setCountdown(Integer countdown)
    212. {
    213. this.countdown = countdown;
    214. }
    215. public Integer getCountdown()
    216. {
    217. return countdown;
    218. }
    219. public void setSavepic(String savepic)
    220. {
    221. this.savepic = savepic;
    222. }
    223. public String getSavepic()
    224. {
    225. return savepic;
    226. }
    227. public MultipartFile getPicNmFile() {
    228. return picNmFile;
    229. }
    230. public void setPicNmFile(MultipartFile picNmFile) {
    231. this.picNmFile = picNmFile;
    232. }
    233. @Override
    234. public String toString() {
    235. return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
    236. .append("id", getId())
    237. .append("picNm", getPicNm())
    238. .append("picAddr", getPicAddr())
    239. .append("picDesc", getPicDesc())
    240. .append("picShow", getPicShow())
    241. .append("creater", getCreater())
    242. .append("createDt", getCreateDt())
    243. .append("modifier", getModifier())
    244. .append("modifyDt", getModifyDt())
    245. .append("remarks", getRemarks())
    246. .append("deleteFlag", getDeleteFlag())
    247. .append("picHyperlink", getPicHyperlink())
    248. .append("adType", getAdType())
    249. .append("linkType", getLinkType())
    250. .append("countdown", getCountdown())
    251. .append("savepic", getSavepic())
    252. .toString();
    253. }
    254. }

     有些包名和类型没处理干净,如果有人使用请注意修改

    在sys_menu中有路由参数配置,请参考ruoyi官网配置,vue组件中有用到

    需要用到ipUtils这个在若依里面有,就是用来获取当前服务的地址和端口号+服务名的

    最后还需要修改若依框架的上传代码中文件路径的参数设置

  • 相关阅读:
    DDD领域驱动设计-子域
    刚刚:2023阿里云双十一优惠活动上线了!
    Mac 下安装PostgreSQL经验
    春眠不觉晓,Java数据类型知多少?基础牢不牢看完本文就有数了
    FPGA学习之实现PID算法
    Vue的自定义事件(Custom Events):实现组件间通信的强大工具
    小红书账号管理软件,批量账号发布笔记!
    java毕业设计医院管理系统Mybatis+系统+数据库+调试部署
    Android程序设计之学校疫情防控管理
    牛视系统源码定制开发come here,抖音矩阵系统。
  • 原文地址:https://blog.csdn.net/weixin_44531193/article/details/126490203