<javascript><css><nodejs>使用express构建一个本地服务器,使用http获取服务器图片和视频,网页端grid布局显示

前言

本文是一个前端示例,是基于nodejs使用express构建一个简单的服务器,并扫描本地资源文件夹,获取视频和图片,网页端通过http来向服务器请求,并将返回的资源在网页端程序,布局为grid。

环境配置

系统:windows

平台:visual studio code

语言:javascript、html、css

库:nodejs

概述

本文是一个前端学习的示例,主要的内容是构建一个服务端,网页端通过路由来访问服务端资源。

代码实现

我们首先创建一个项目文件夹,并在文件夹下新建一个js文件,名为server.js,用于编写服务端代码。

本例中我们使用express来构建服务器:

javascript 复制代码
const express = require('express');
const fs = require('fs');
const path = require('path');


const app = express();
const PORT = 3000;
const videoFolder = path.join(__dirname, 'videos');
const thumbnailFolder = path.join(__dirname, 'thumbnails'); // 假设有预生成的封面图片
console.log(thumbnailFolder);
const supportedExtensions = ['.mp4', '.avi', '.mov', '.mkv']; // 支持的视频格式
const defaultThumbnail='./thumbnails/default.png';

//构建静态文件目录
app.use(express.static(path.join(__dirname,'public')));
app.use('/videos',express.static(path.join(__dirname,'videos')));

function getfiletype(filePath){
    const ext=path.extname(filePath).toLowerCase();
    const typeMap={
        '.mp4':'video',
        '.avi':'video',
        '.mov':'video',
        '.mkv':'video',
        '.png':'image',
        '.jpg':'image',
        '.txt':'text',
        '.json':'json'

    };
    return typeMap[ext] || 'unknown';
}

/**
 *扫描资源文件夹
 * @param {*} folderPath 
 * @param {*} files 
 * @returns 
 */
function getallfile(folderPath,files=[]){
    const items=fs.readdirSync(folderPath);
    const result=[];
    items.forEach(item=>{
        const itemPath=path.join(folderPath,item);
        const stat=fs.statSync(itemPath);
        if(stat.isDirectory()){
           const subresult={
            videos:[],
            images:[],
            texts:[],
            jsons:[]
           };
           const subitmes=fs.readdirSync(itemPath);
           subitmes.forEach(subitem=>{
               const subitempath=path.join(itemPath,subitem);
               //console.log(subitempath);
               const subitem2=path.join('videos',item,subitem);
               //console.log(item);
               //console.log(subitem2);
               const substat=fs.statSync(subitempath);
               if (substat.isFile()){
                    const filetype=getfiletype(subitempath);
                    if (filetype === 'video'){
                        subresult.videos.push(subitem2);
                        //generatethmbnail(item,subitem,'00:00:02.000');
                    } else if (filetype === 'image'){
                        subresult.images.push(subitem2);
                    }   else if( filetype === 'text'){
                        subresult.texts.push(subitem2);
                    } else if( filetype === 'json'){
                        subresult.jsons.push(subitem2);
                    }
               }
           });
    
           result.push(subresult);
        }

    });
    
    return result;
    
}

app.get('/api/videos', (req, res) => {
    const allfiles=getallfile(videoFolder);
    //console.log(allfiles);
    res.json(allfiles);
});
app.get('/play', (req, res) => {
    
    loadfile('./public/play.html',res);


})

app.get('/video',(req,res)=>{
    const videoname=req.query.name;
    const videoPath = path.join(__dirname, videoname);

    //console.log(videoPath);
    const stat = fs.statSync(videoPath);
    const fileSize = stat.size;
    const range = req.headers.range;
 
    if (range) {
        const parts = range.replace(/bytes=/, "").split("-");
        const start = parseInt(parts[0], 10);
        const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
 
        const chunkSize = (end - start) + 1;
        const file = fs.createReadStream(videoPath, { start, end });
        const head = {
            'Content-Range': `bytes ${start}-${end}/${fileSize}`,
            'Accept-Ranges': 'bytes',
            'Content-Length': chunkSize,
            'Content-Type': 'video',
        };
 
        res.writeHead(206, head);
        file.pipe(res);
    } else {
        const head = {
            'Content-Length': fileSize,
            'Content-Type': 'video/mp4',
        };
        res.writeHead(200, head);
        fs.createReadStream(videoPath).pipe(res);
    }
})
/**
   * 读取html文件内容
   * @param {*} filePath 
   */
function loadfile(filePath,res){
    // 解析文件扩展名
    const extname = path.extname(filePath);
    let contentType = 'text/html';

    switch (extname) {
        case '.css':
            contentType = 'text/css';
            break;
        case '.js':
            contentType = 'text/javascript';
            break;
        case '.json':
            contentType = 'application/json';
            break;
        case '.png':
            contentType = 'image/png';
            break;
        case '.jpg':
            contentType = 'image/jpg';
            break;
        // 可以根据需要添加更多的MIME类型
    }

    // 读取文件内容
    fs.readFile(filePath, (error, content) => {
        if (error) {
            if (error.code == 'ENOENT') {
                // 文件未找到,返回404
                res.writeHead(404, { 'Content-Type': contentType });
                res.end('File not found', 'utf-8');
            } else {
                // 服务器错误,返回500
                res.writeHead(500);
                res.end('Sorry, check with the site admin for error: ' + error.code + ' ..\n', 'utf-8');
            }
        } else {
            // 文件读取成功,返回文件内容
            res.writeHead(200, { 'Content-Type': contentType });
            res.end(content, 'utf-8');
            
        };
        
    });
  };
  
app.listen(PORT,'localhost', () => {
  console.log(`Server is running on port http://localhost:${PORT}`);
});

然后在项目文件夹下,创建public文件夹,配置为静态文件目录,在public文件夹下,新建一个index.html文件:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Video Folder Scanner</title>
  <link rel="stylesheet" href="styles.css">
  <style>
    .thumbnail {
      width: 150px;
      height: 100px;
      object-fit: cover;
      cursor: pointer;
      margin: 10px;
    }
  </style>
</head>
<body>
  <h1>Video Folder</h1>
  <div id="thumbnails" class="thumbnails-container">
    
  </div>
  
  <script src="./index.js"></script>
</body>
</html>

再创建一个styles.css文件和一个index.js文件。
index.js

javascript 复制代码
document.addEventListener('DOMContentLoaded', () => {
    const urls=[];
    async function getallurl(){
    
        const response=await fetch('http://localhost:3000/api/videos');
        const data=await response.json();
        return data;
  
    }  
   

    async function gettext(){
        const fileinfos=await getallurl();
        console.log(fileinfos);

        for(const fileinfo of fileinfos){
            console.log(fileinfo);
            const videourl=fileinfo.videos[0];
            const imgurl=fileinfo.images[0];
            const jsonurl=fileinfo.jsons[0];
            const response = await fetch(`http://localhost:3000/${jsonurl}`);
            if (!response.ok) {
                throw new Error('Network response was not ok');
            }
            const data = await response.json();

            const thumbnails=document.getElementById('thumbnails');

            const div1=document.createElement('div');
            div1.className='div1';

            const infodiv=document.createElement('div');
            infodiv.className='infodiv';

            const imgdiv=document.createElement('div');
            imgdiv.className='imgdiv';

            const img=document.createElement('img');

            const videoname=data['name'];
            const p1=document.createElement('p');
            p1.textContent=`${videoname}`;
            
            img.className='img';
            img.src=imgurl;
            img.addEventListener('click',()=>{
                //window.location.href= `${video.videoUrl}`;
                window.location.href=`./play?path=${videourl}&data=${jsonurl}`;
            });
           
            imgdiv.appendChild(img);
            imgdiv.appendChild(p1);

            thumbnails.appendChild(imgdiv);
           
            console.log(data);
        }
        };
    gettext();
  });

styles.css

css 复制代码
.thumbnails-container{
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); /* 自动填充列,最小宽度200px */
    gap: 20px; /* 网格项之间的间距 */
}


.imgdiv{

    margin: 10px;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 8px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    transition: transform 0.2s;
    overflow: hidden;
}
.imgdiv:hover{
    background-color: #a5dff7;
    transform: scale(1.05);
}
.img{
    width: 100%;
    height: auto;
    display: block;
    border-radius: 8px;
}

/* 针对不同屏幕尺寸调整布局 */
@media (max-width: 1200px) {
    .imgdiv {
        width: calc(33.333% - 40px); /* 3列布局,减去间距 */
    }
}
 
@media (max-width: 800px) {
    .imgdiv {
        width: calc(50% - 40px); /* 2列布局,减去间距 */
    }
}
 
@media (max-width: 500px) {
    .imgdiv {
        width: calc(80% - 40px); /* 单列布局,减去间距 */
    }
}

当我们启动服务器后,并访问http://localhost:3000/,网页端显示如下(仅供参考):

此处,图片与显示名称,依旧实际文件夹内的内容来,以上是我测试时使用的资源文件夹内的图片等内容。

图片相当于是封面图,点击图片后,会跳转到另一个页面:

此处,要播放的视频,是在服务器的路由中已经配置的,服务器端会根据请求的视频名称,采用流式媒体播放视频。

注:本文是一个记录,用于以后参考,如果刚好对你也有帮助,那么也是不错的。

相关推荐
问道飞鱼31 分钟前
【前端知识】Javascript进阶-类和继承
开发语言·前端·javascript·继承·
Grocery store owner35 分钟前
el-time-picker选择时分秒并且根据总秒数禁用不可选
前端·javascript·vue.js
川石教育39 分钟前
Vue前端开发-axios默认配置和响应结构
前端·javascript·vue.js
JackieDYH1 小时前
Vue3中页面滑到最下面,然后跳转新页面后新页面的位置还是在之前浏览的位置
前端·javascript·html
正小安1 小时前
vue3实现ai聊天对话框
前端·javascript·vue.js·elementui
长风清留扬1 小时前
小程序开发中的插件生态与应用-上
前端·javascript·css·微信小程序·小程序·apache
付春员2 小时前
根据PDF模板单个PDF导出到浏览器和多个PDF打包ZIP导出到浏览器
java·javascript·pdf
Hopebearer_2 小时前
网络请求与远程资源之XMLHttpRequest Level 1和2(一)
前端·javascript·网络·ajax·ecmascript
阿虎-cqh3 小时前
HTML CSS JS实现图书馆档案管理
javascript·css·html