Socket.io io.emit() 不起作用,从服务器到客户端的通信不起作用。需要一种通过 io.emit() 从服务器向客户端发送数据的方法吗?

问题描述 投票:0回答:1

我制作的程序是一个文档转换器,它接收文档并使用 libreoffice 将其转换为 pdf。

这是我的服务器(localhost:3000)

import express from "express";
import bodyParser from "body-parser";
import multer from "multer"; //To communicate files
import { dirname, extname } from "path";
import { fileURLToPath } from "url";
import fs from "fs";
import { exec } from "child_process"; //Used to execute shell commands
import util from "util"; //built-in Node.js module that provides utility functions.

import { createServer } from 'node:http';
import { Server } from 'socket.io';



const execPromise = util.promisify(exec); // creates a Promise-based version of the exec function to use with asynchronous operations.


const __dirname = dirname(fileURLToPath(import.meta.url)); //To get the whole path upto working directory

const TestServer = express();
const port = 3000;

const server = createServer(TestServer);
const io = new Server(server);


TestServer.use(bodyParser.urlencoded({ extended: true }));
TestServer.use(express.static("public"));



let docName;

const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, 'public/Document');
    },
    filename: (req, file, cb) => {
        //console.log(file);
        docName = file.originalname;
        cb(null, file.originalname);
    }
});

const upload = multer({ storage: storage });

TestServer.use((err, req, res, next) => {
    if (err instanceof multer.MulterError) {
        // A Multer error occurred (e.g., file size exceeded)
        res.status(400).send('Multer error: ' + err.message);
    } else if (err) {
        // An unknown error occurred
        res.status(500).send('Unknown error: ' + err.message);
    } else {
        // No error occurred, proceed to the next middleware or route handler
        next();
    }
});

//function to check if a particular file exists
function fileExists(filePath) {
    try {
        fs.accessSync(filePath, fs.constants.F_OK);
        return true;
    } catch (err) {
        return false;
    }
}


let pathToOffice;

const osPlatform = process.platform;

if (osPlatform === 'win32' || osPlatform === 'win64') {
    pathToOffice = 'C:\\Program Files\\LibreOffice\\program\\soffice.exe';
} else if (osPlatform === 'darwin') {
    pathToOffice = '/Applications/LibreOffice.app/Contents/MacOS/soffice';
} else if (osPlatform === 'linux') {
    pathToOffice = '' // have to enter the path according to how soffice is in linux //TODO
} else {
    console.error('Unsupported operating system');
    process.exit(1);
}


async function convertWordToPdf(inputPath, outputPath) {
    try {
        // Convert using LibreOffice
        const { stdout, stderr } = await execPromise(`"${pathToOffice}" --headless --invisible --convert-to pdf --outdir "${outputPath}" "${inputPath}"`);
        if (stderr && !stderr.includes('Secure coding is not enabled for restorable state!')) {
            throw new Error(stderr);
        }
        //console.log('Conversion successful:', stdout);

    } catch (error) {
        console.error('Error converting Word to PDF:', error);
        throw error;
    }
}



TestServer.get("/", (req, res) => {
    //send homepage
    res.render("uploadDoc.ejs");
});


TestServer.post("/getdocument", upload.fields([
    { name: 'doc-upload', maxCount: 1 }
]), async (req, res) => {
    console.log(`Document successfully uploaded: ${docName}`);

    //console.log(req.body);

    console.log("---------------------------------------------------------------");


    io.emit('uploadStatus', { status: 'uploading' });
    //Convert process below and send the file
    try {
        if (fileExists(__dirname + `/public/Document/${docName}`)) {
            const docNameWithoutExtension = docName.replace(extname(docName), '');
            io.emit('uploadStatus', { status: 'converting' });
            await convertWordToPdf(__dirname + `/public/Document/${docName}`, __dirname + `/public/Document/${docNameWithoutExtension}`);
            io.emit('uploadStatus', { status: 'complete' });
            res.render("downloadDoc.ejs", {
                filepath: `/Document/${docNameWithoutExtension}/${docNameWithoutExtension}.pdf`
            });
        }
        else {
            res.send("<h1>UNSUCCESSFUL: File not found</h1>");
        }
    } catch (error) {
        console.error("Failed to make conversion:", error.message);
        res.send("<h1>UNSUCCESSFUL: Conversion failed</h1>");
    }


});


io.on('connection', (socket) => {
    console.log('a user connected');

    // Send initial status to connected client
    socket.on('formSubmit', (msg) => {
        console.log(msg.data);
        io.emit('uploadStatus', { status: 'idle' });

    });


    socket.on('disconnect', () => {
        console.log('user disconnected');
    });
});


server.listen(port, () => {
    console.log(`Listening on port ${port}`);
});


下面是我正在上传文档的客户端

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Upload Doc</title>
    <link rel="stylesheet" href="/styles/styles.css">
</head>

<body class="m-0 p-0">

    <div class="bouncing-blobs-container">
        <div class="bouncing-blobs-glass"></div>
        <div class="bouncing-blobs">
            <div class="bouncing-blob bouncing-blob--blue"></div>
            <div class="bouncing-blob bouncing-blob--blue"></div>
            <div class="bouncing-blob bouncing-blob--blue"></div>
            <div class="bouncing-blob bouncing-blob--white"></div>
            <div class="bouncing-blob bouncing-blob--purple"></div>
            <div class="bouncing-blob bouncing-blob--purple"></div>
            <div class="bouncing-blob bouncing-blob--pink"></div>
        </div>
    </div>



    <div class="px-36 py-4 h-screen w-screen">
        <form id="form" action="/getdocument" method="post" enctype="multipart/form-data">
            <div class="space-y-12">
                <label for="document-upload-area" class="block text-sm font-medium leading-6 text-gray-900">Document
                    Upload</label>

                <div class="mt-2 flex gap-4 justify-center">
                    <p class="text-sm self-center">Convert</p>
                    <select id="convert-from" name="convert-from" required
                        class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:max-w-xs sm:text-sm sm:leading-6">
                        <option value="" disabled selected>--Select--</option>
                        <option value="doc">DOCX, DOC</option>
                        <option value="other-format">Other Format</option>
                    </select>

                    <p class="text-sm self-center">to</p>

                    <select id="convert-to" name="convert-to" required
                        class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:max-w-xs sm:text-sm sm:leading-6">
                        <option value="" disabled selected>--Select--</option>
                        <option value="pdf">PDF</option>
                    </select>
                </div>

                <div class="mt-2 flex justify-center rounded-lg border border-dashed border-gray-900/25 px-6 py-10">
                    <div>
                        <svg class="mx-auto" xmlns="http://www.w3.org/2000/svg"
                            xmlns:xlink="http://www.w3.org/1999/xlink" width="40" zoomAndPan="magnify"
                            viewBox="0 0 30 30.000001" height="40" preserveAspectRatio="xMidYMid meet" version="1.0">
                            <defs>
                                <clipPath id="id1">
                                    <path
                                        d="M 2.742188 3.484375 L 27.417969 3.484375 L 27.417969 25.257812 L 2.742188 25.257812 Z M 2.742188 3.484375 "
                                        clip-rule="nonzero" />
                                </clipPath>
                            </defs>
                            <g clip-path="url(#id1)">
                                <path fill="rgb(8.239746%, 42.349243%, 86.669922%)"
                                    d="M 15.082031 3.484375 L 2.742188 5.902344 L 2.742188 22.839844 L 15.082031 25.257812 Z M 17.546875 5.902344 L 17.546875 8.324219 L 20.015625 8.324219 L 20.015625 10.742188 L 17.546875 10.742188 L 17.546875 13.160156 L 20.015625 13.160156 L 20.015625 15.582031 L 17.546875 15.582031 L 17.546875 18 L 20.015625 18 L 20.015625 20.417969 L 17.546875 20.417969 L 17.546875 22.839844 L 27.417969 22.839844 L 27.417969 5.902344 Z M 22.484375 8.324219 L 24.953125 8.324219 L 24.953125 10.742188 L 22.484375 10.742188 Z M 5.425781 9.890625 L 7.621094 9.890625 L 8.757812 12.570312 C 8.851562 12.789062 8.921875 13.042969 8.992188 13.332031 L 9.023438 13.332031 C 9.066406 13.160156 9.148438 12.894531 9.273438 12.550781 L 10.542969 9.890625 L 12.542969 9.890625 L 10.15625 14.332031 L 12.613281 18.851562 L 10.480469 18.851562 L 9.105469 15.929688 C 9.054688 15.828125 8.992188 15.621094 8.941406 15.332031 L 8.921875 15.332031 C 8.890625 15.46875 8.832031 15.675781 8.738281 15.953125 L 7.351562 18.851562 L 5.210938 18.851562 L 7.753906 14.367188 Z M 22.484375 13.160156 L 24.953125 13.160156 L 24.953125 15.582031 L 22.484375 15.582031 Z M 22.484375 18 L 24.953125 18 L 24.953125 20.417969 L 22.484375 20.417969 Z M 22.484375 18 "
                                    fill-opacity="1" fill-rule="nonzero" />
                            </g>
                        </svg>
                        <div class="mt-4 flex text-sm leading-6 text-gray-600 justify-center">
                            <label for="doc-upload"
                                class="relative cursor-pointer rounded-md bg-transparent font-semibold text-indigo-600 focus-within:outline-none focus-within:ring-2 focus-within:ring-indigo-600 focus-within:ring-offset-0 hover:text-indigo-500">
                                <span>Upload a file</span>
                                <input id="doc-upload" name="doc-upload" type="file" class="sr-only">
                            </label>
                            <p class="pl-1">or drag and drop</p>
                        </div>
                        <p id="doc-name" class="text-lg leading-5 text-gray-600 text-center"></p>
                    </div>
                </div>
            </div>

            <div id="buttons" class="mt-6 justify-center gap-x-6 hidden">
                <a href="/" class="flex justify-center items-center">
                    <button type="button" class="text-sm font-semibold leading-6 text-gray-900">Cancel</button>
                </a>
                <button id="submit-btn" type="submit"
                    class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Convert</button>
            </div>
        </form>
    </div>


    <script type="text/javascript">
        document.getElementById('doc-upload').addEventListener('change', function () {
            // File has been selected, show the buttons
            document.getElementById('buttons').classList.remove("hidden");
            document.getElementById('buttons').classList.add("flex");


            // Display the selected file name
            const fileNameElement = document.getElementById('doc-name');
            const fileInput = document.getElementById('doc-upload');

            if (fileInput.files.length > 0) {
                fileNameElement.textContent = fileInput.files[0].name;
            } else {
                // Reset the text if no file is selected
                fileNameElement.textContent = 'word DOCX and DOC';
            }
        });

        //Animation taken from a youtube video which i cant remember.

        const MIN_SPEED = 1.5
        const MAX_SPEED = 2.5

        function randomNumber(min, max) {
            return Math.random() * (max - min) + min
        }

        class Blob {
            constructor(el) {
                this.el = el
                const boundingRect = this.el.getBoundingClientRect()
                this.size = boundingRect.width
                this.initialX = randomNumber(0, window.innerWidth - this.size)
                this.initialY = randomNumber(0, window.innerHeight - this.size)
                this.el.style.top = `${this.initialY}px`
                this.el.style.left = `${this.initialX}px`
                this.vx =
                    randomNumber(MIN_SPEED, MAX_SPEED) * (Math.random() > 0.5 ? 1 : -1)
                this.vy =
                    randomNumber(MIN_SPEED, MAX_SPEED) * (Math.random() > 0.5 ? 1 : -1)
                this.x = this.initialX
                this.y = this.initialY
            }

            update() {
                this.x += this.vx
                this.y += this.vy
                if (this.x >= window.innerWidth - this.size) {
                    this.x = window.innerWidth - this.size
                    this.vx *= -1
                }
                if (this.y >= window.innerHeight - this.size) {
                    this.y = window.innerHeight - this.size
                    this.vy *= -1
                }
                if (this.x <= 0) {
                    this.x = 0
                    this.vx *= -1
                }
                if (this.y <= 0) {
                    this.y = 0
                    this.vy *= -1
                }
            }

            move() {
                this.el.style.transform = `translate(${this.x - this.initialX}px, ${this.y - this.initialY
                    }px)`
            }
        }

        function initBlobs() {
            const blobEls = document.querySelectorAll('.bouncing-blob')
            const blobs = Array.from(blobEls).map((blobEl) => new Blob(blobEl))

            function update() {
                requestAnimationFrame(update)
                blobs.forEach((blob) => {
                    blob.update()
                    blob.move()
                })
            }

            requestAnimationFrame(update)
        }

        initBlobs()

        

    </script>

</body>

</html>

我尝试集成 socket.io 以根据文档上传时间、转换开始时间和完成时间向客户端发送更新。但我找不到将数据发送到客户端的方法。从客户端,我可以通过触发事件发送一些数据,但这不是我想要的。我基本上需要服务器能够以某种方式将更新发送到客户端。

后端使用NodeJS、Express完成。前端使用EJS、tailwind将数据从服务器渲染到客户端。

javascript node.js express socket.io ejs
1个回答
0
投票

有几点您需要考虑。

客户端没有实现socket客户端。 您刚刚在服务器端创建了socket.io,您需要在客户端实现连接。

我对服务器实现也持保留态度。您应该创建一个单独的 socket.io 服务器并连接到该服务器,并且 Express API 应该是单独的。

您的客户端实现应该如下所示。

<script src="http://localhost:4000/socket.io/socket.io.js"></script>
<script>
    var socket = io('http://localhost:4000', {
        transports: [ 'websocket' ],
        'reconnection': true,
        'reconnectionDelay': 500,
        'reconnectionDelayMax': 1000,
        'reconnectionAttempts': 100
    });

    socket.on('connect', onConnect);
    socket.on('disconnect',onDisconnect);
    
    socket.on('connect_error', function(){
        console.log('Connection Failed');
    });

function onConnect(){
    console.log('Connected Successully...'); 
}
function onDisconnect(){
    console.log('Client disconnected');
}
</script>

一旦实现了与 socket.io 服务器的客户端连接。现在服务器应该能够将事件发送到客户端。 但就你的情况而言,这是行不通的。您想要使用 webapi 调用来调用 Socket.io 事件。

为此,您需要实现“socket.io-emitter”

您可以从

获取更多详情

https://www.npmjs.com/package/socket.io-emitter

或来自socket.io本身的更新版本(推荐)

https://github.com/socketio/socket.io-redis-emitter

然后需要管理客户端的SocketID。将其作为附加参数发送到您的 API 帖子,因为您的 API 不知道发送事件的 SocketID,

您可以在连接函数中获取客户端套接字 ID,例如(在客户端)

function onConnect(){
    console.log('Client Connected Successully...'); 
     let ClientSocketID= socket.id;

}

在您的 API 中,您需要使用“socket.io-emitter”或 更新版本“@socket.io/redis-emitter”

您需要安装redis服务器,https://redis.io/

并且需要安装redis客户端 npm 安装 redis。

https://www.npmjs.com/package/redis

const { Emitter } = require("@socket.io/redis-emitter");
const { createClient } = require("redis"); 

const redisClient = createClient();

const io = new Emitter(redisClient);

io.to(CLIENT_SOCKET_ID).emit('EVENTNAME,SOME-JSON-Object);

总结: 1:创建2个独立的项目webapis和Socket.io服务器

2:在客户端实现socket.io客户端并连接到socket.io服务器。

3:在 API 中实现 socket.io-redis-emitter 并将客户端 SocketID 传递给它。并发出

© www.soinside.com 2019 - 2024. All rights reserved.