Práctica de carga de archivos front-end y procesamiento back-end: carga de archivos en bloques

La carga de archivos es una de las características comunes en las aplicaciones web modernas. En este blog, exploraremos una implementación de carga de archivos front-end simple pero completa y, al mismo tiempo, brindaremos un ejemplo de back-end que demuestra cómo manejar los archivos cargados. Usaremos JavaScript como lenguaje front-end combinado con Node.js como entorno back-end. ¡Empecemos!

Carga de archivos front-end

Centrémonos primero en la parte delantera. Usaremos HTML, CSS y JavaScript para crear un formulario de carga de archivos simple y luego pasaremos el archivo a un servidor backend a través de AJAX. Aquí asumimos que ya tiene conocimientos básicos de desarrollo front-end.

1. Estructura HTML

Primero, creamos una estructura HTML que incluye un cuadro de entrada de carga de archivos, un botón de carga y elementos para mostrar el progreso y la información de la carga:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>文件上传示例</title>
</head>
<body>
  <input type="file" id="videoUploader" accept="video/mp4, video/ogg">
  <button id="uploadBtn">上传</button>
  <p id="uploadInfo"></p>
  <progress id="uploadProgress" value="0" max="100"></progress>
</body>
</html>

2. Estilos CSS (opcional)

Puede agregar algunos estilos CSS para embellecer la página según sea necesario.

3. Lógica de JavaScript

A continuación, usaremos JavaScript para implementar la lógica de carga de archivos. Comenzamos con un archivo de configuración, luego implementamos la función de carga y las funciones auxiliares relacionadas:

// config.js
// 导出一些常量,包括上传信息、允许上传的文件类型、块大小和上传接口的URL
// 这些常量将在前端和后端代码中使用
const BASE_URL = 'http://localhost:8000/';

export const UPLOAD_INFO = {
  'NO_FILE': '请先选择文件',
  'INVALID_TYPE': '不支持该类型文件上传',
  'UPLOAD_FAILED': '上传失败',
  'UPLOAD_SUCCESS': '上传成功'
}

export const ALLOWED_TYPE = {
  'video/mp4': 'mp4',
  'video/ogg': 'ogg'
}

export const CHUNK_SIZE = 64 * 1024;

export const API = {
  UPLOAD_VIDEO: BASE_URL + 'upload_video'
}

// index.js
// 在闭包内部,定义一个立即执行的匿名函数,接受document作为参数
((doc) => {
  // 获取页面上的一些元素
  const oProgress = doc.querySelector('#uploadProgress'); // 上传进度条
  const oUploader = doc.querySelector('#videoUploader'); // 文件上传输入框
  const oInfo = doc.querySelector('#uploadInfo'); // 用于显示上传信息
  const oBtn = doc.querySelector('#uploadBtn'); // 上传按钮

  let uploadedSize = 0; // 已上传的文件大小,用于控制分块上传

  // 初始化函数
  const init = () => {
    bindEvent(); // 绑定事件
  }

  // 绑定事件函数
  function bindEvent() {
    oBtn.addEventListener('click', uploadVideo, false); // 点击上传按钮触发上传函数
  }

  // 异步上传函数
  async function uploadVideo() {
    // 获取文件对象
    const { files: [ file ] } = oUploader;
    
    if (!file) {
      // 检查是否选择了文件,如果没有则显示提示信息
      oInfo.innerText = UPLOAD_INFO['NO_FILE'];
      return;
    }

    if (!ALLOWED_TYPE[file.type]) {
      // 检查文件类型是否被允许,如果不允许则显示提示信息
      oInfo.innerText = UPLOAD_INFO['INVALID_TYPE'];
      return;
    }

    // 获取文件的一些基本信息
    const { name, type, size } = file;
    const fileName = new Date().getTime() + '_' + name;
    let uploadedResult = null;
    oProgress.max = size;
    oInfo.innerText = '';

    while (uploadedSize < size) {
      // 使用slice方法将文件切割成小块,实现分块上传
      const fileChunk = file.slice(uploadedSize, uploadedSize + CHUNK_SIZE);
      // 创建FormData对象,用于传递文件块和相关信息
      const formData = createFormData({
        name, 
        type,
        size,
        fileName,
        uploadedSize,
        file: fileChunk
      });

      try {
        // 使用axios库发送POST请求,将文件块上传到后端
        uploadedResult = await axios.post(API.UPLOAD_VIDEO, formData);
      } catch (e) {
        // 捕获异常,如果上传失败,则显示提示信息
        oInfo.innerText = `${ UPLOAD_INFO['UPLOAD_FAILED'] }(${ e.message })`;
        return; 
      }

      // 更新已上传的文件大小和进度条
      uploadedSize += fileChunk.size;
      oProgress.value = uploadedSize;
    }

    // 上传成功后,显示上传成功的提示信息,并将文件输入框置空
    oInfo.innerText = UPLOAD_INFO['UPLOAD_SUCCESS'];
    oUploader.value = null;
    // 根据后端返回的视频URL,创建一个视频元素并展示在页面上
    createVideo(uploadedResult.data.video_url);
  }

  // 创建FormData函数,将文件块和相关信息添加到FormData对象中
  function createFormData ({
    name, 
    type,
    size,
    fileName,
    uploadedSize,
    file
  }) {
    const fd = new FormData();

    fd.append('name', name);
    fd.append('type', type);
    fd.append('size', size);
    fd.append('fileName', fileName);
    fd.append('uploadedSize', uploadedSize);
    fd.append('file', file);

    return fd;
  }

  // 创建视频元素函数,用于在页面上展示上传成功的视频
  function createVideo(src) {
    const oVideo = document.createElement('video');
    oVideo.controls = true;
    oVideo.width = '500';
    oVideo.src = src;
    document.body.appendChild(oVideo);
  }

  init(); // 初始化,执行绑定事件函数
})(document);
  1. Dependencias de importación:

    • config.js: Exporte algunas constantes, incluida la información de carga, los tipos de archivos que se pueden cargar, el tamaño del bloque y la URL de la interfaz de carga.
    • axios: una biblioteca para realizar solicitudes HTTP para cargar fragmentos de archivos al backend.
  2. Cree una función anónima que se ejecute inmediatamente:

    • Acepta documentcomo argumento y lo utiliza docpara representar documentel objeto.
    • Este enfoque evita la contaminación variable global y garantiza que el código se ejecute en un ámbito separado.
  3. Obtener elementos en la página:

    • oProgress: Un elemento que representa la barra de progreso de la carga <progress>.
    • oUploader: Un elemento que representa el cuadro de entrada de carga de archivos <input type="file">.
    • oInfo: Un elemento utilizado para mostrar información de carga <p>.
    • oBtn: El elemento que representa el botón de carga <button>.
  4. Función de inicialización:

    • Llame bindEvent()a la función, que se utiliza para vincular el controlador de eventos cuando se hace clic en el botón de carga.
  5. Función de evento vinculante:

    • Usando addEventListener()el método, agregue un detector de eventos de clic para el botón de carga.
    • Cuando se hace clic en el botón cargar, uploadVideo()se activará la función para cargar el archivo.
  6. Función de carga asincrónica uploadVideo():

    • Obtenga el objeto de archivo , que se obtiene filea través del cuadro de entrada de carga de archivos .oUploader
    • Comprueba si un archivo está seleccionado, muestra un mensaje en caso contrario y regresa.
    • Compruebe si se permite cargar el tipo de archivo; de lo contrario, muestre un mensaje y regrese.
    • Obtenga información básica sobre un archivo, como el nombre del archivo, el tipo de archivo y el tamaño del archivo.
    • Usando whileun bucle, cargue el archivo en trozos:
      • Utilice file.slice()el método para cortar el archivo en trozos pequeños (el tamaño del trozo CHUNK_SIZEestá definido por una constante).
      • Cree FormDataun objeto al que agregar fragmentos de archivos e información relacionada para pasarlos al backend.
      • Usando axios.post()el método, cargue fragmentos del archivo a la URL especificada por el backend ( API.UPLOAD_VIDEOdefinida por una constante).
      • Si la carga falla, detecte la excepción, muestre un mensaje y regrese.
      • Actualice el tamaño del archivo cargado y el valor de la barra de progreso.
    • Una vez completada la carga, se muestra un mensaje que indica que la carga se realizó correctamente y el valor del cuadro de entrada del archivo se establece en blanco.
    • De acuerdo con la URL del video devuelta por el backend, llame createVideo()a la función para crear un elemento de video en la página y mostrar el video cargado correctamente.
  7. Crear FormDatafunción createFormData():

    • Agrega fragmentos de archivos e información relacionada a FormDataun objeto para pasarlos al backend.
  8. Crear función de elemento de vídeo createVideo():

    • Se utiliza para crear un elemento de video en la página y establecer la URL del video en la URL del video devuelta por el backend después de que la carga se haya realizado correctamente.
  9. Función de inicialización init():

    • Ejecute bindEvent()la función para vincular el botón de carga y el controlador de eventos.
  10. Finalmente, init()todo el código front-end se inicia llamando a una función.

 

Incluimos axiosla biblioteca en el código de front-end, que es una herramienta común para realizar solicitudes HTTP. En un proyecto real, debe asegurarse de importar la biblioteca en el archivo HTML axioso utilizar otras bibliotecas con funciones similares.

En el código anterior, primero definimos algunas constantes, como los tipos de archivos que se pueden cargar, el tamaño del bloque, etc. Luego capturamos el evento de clic del botón "cargar" a través del monitoreo de eventos y ejecutamos la función de carga. En la función de carga, cortamos el archivo en varios pedazos pequeños cargándolos en pedazos y los enviamos al backend uno por uno para su procesamiento. Durante el proceso de carga, también actualizaremos la barra de progreso de carga y mostraremos la información de carga en tiempo real.

Procesamiento de archivos de fondo

Ahora centrémonos en la parte del procesamiento backend. Usaremos Node.js y Express para configurar un servidor backend simple que toma fragmentos de archivos del frontend y los fusiona en archivos completos.

1. Instalar dependencias

Primero, cree un archivo en el directorio del proyecto package.jsony luego ejecute el siguiente comando para instalar las dependencias:

El código anterior utiliza el marco Express para construir un servidor simple y establecer la ruta para la carga de archivos. Podemos /upload_videocargar bloques de archivos a través de la interfaz y el backend guardará los bloques de archivos en el lado del servidor de acuerdo con la información cargada hasta que todos los bloques se reciban y combinen en un archivo completo.

npm init -y
npm install express body-parser express-fileupload

2. Código de fondo

A continuación, creamos un server.jsarchivo y escribimos el código de backend:

const express = require('express');
const bodyParser = require('body-parser');
const uploader = require('express-fileupload');
const { extname, resolve } = require('path');
const { existsSync, appendFileSync, writeFileSync } = require('fs');

const app = express();
const PORT = 8000;

// 设置中间件,解析请求的JSON数据和文件上传
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(uploader());

// 设置静态资源目录,用于存放上传的临时文件块
app.use('/', express.static('upload_temp'));

// 允许跨域访问
app.all('*', (req, res, next) => {
  res.header('Access-Control-Allow-origin', '*');
  res.header('Access-Control-Allow-Methods', 'POST,GET');
  next();
});

// 处理文件上传的POST请求,对应的URL为/upload_video
app.post('/upload_video', (req, res) => {
  // 从请求体中获取传递的文件信息和文件块
  const { name, type, size, fileName, uploadedSize } = req.body;
  const { file } = req.files;

  if (!file) {
    // 检查是否传递了文件块,如果没有则返回错误信息
    res.send({
      code: 1001,
      msg: 'No file uploaded'
    });
    return;
  }

  if (!ALLOWED_TYPE[type]) {
    // 检查文件类型是否被允许,如果不允许则返回错误信息
    res.send({
      code: 1002,
      msg: 'The type is not allowed for uploading.'
    });
    return;
  }

  const filename = fileName + extname(name);
  const filePath = resolve(__dirname, './upload_temp/' + filename);

  if (uploadedSize !== '0') {
    if (!existsSync(filePath)) {
      // 检查文件是否存在,如果不存在则返回错误信息
      res.send({
        code: 1003,
        msg: 'No file exists'
      });
      return;
    }

    // 文件已存在,则将当前文件块追加到已有的文件中
    appendFileSync(filePath, file.data);

    // 返回成功信息和视频URL
    res.send({
      code: 0,
      msg: 'Appended',
      video_url: 'http://localhost:8000/' + filename
    });

    return;
  }
  
  // 第一个文件块,创建一个新文件并写入当前块的数据
  writeFileSync(filePath, file.data);

  // 返回成功信息
  res.send({
    code: 0,
    msg: 'File is created'
  });
});

// 启动服务,监听指定端口
app.listen(PORT, () => {
  console.log('Server is running on ' + PORT);
});
  1. Dependencias de importación:

    • express: Marco Express para construir servidores web.
    • body-parser: analiza los datos JSON en el cuerpo de la solicitud.
    • express-fileupload: Maneja solicitudes de carga de archivos.
    • path: Módulo integrado de Node.js para manejar rutas de archivos.
    • fs: Módulo integrado de Node.js para operaciones del sistema de archivos.
  2. Cree una aplicación Express:

    const app = express();
    const PORT = 8000;
    

  3. Utilice software intermedio:

    • bodyParserEl middleware se utiliza para analizar datos JSON y datos de formulario en el cuerpo de la solicitud.
    • uploaderEl middleware se utiliza para manejar las solicitudes de carga de archivos.
    • Configure el directorio de recursos estáticos '/upload_temp'para almacenar bloques de archivos temporales cargados.
  4. Establecer acceso entre dominios: utilice app.all()el método para configurar los encabezados de respuesta que permitan solicitudes entre dominios.

  5. Manejar solicitudes de carga de archivos:

    • Utilice app.post('/upload_video', ...)la definición para manejar solicitudes POST para cargar archivos y la URL correspondiente es /upload_video.
    • Obtenga la información del archivo pasado y los bloques de archivos del cuerpo de la solicitud, incluido el nombre del archivo, el tipo de archivo, el tamaño del archivo, la posición inicial de carga de los bloques de archivos, etc.
    • Comprueba si el bloque de archivos existe y devuelve un mensaje de error si no existe.
    • Comprueba si el tipo de archivo está permitido y, en caso contrario, devuelve un mensaje de error.
    • Genere un nuevo nombre de archivo según el nombre y el tipo de archivo, y calcule la ruta de almacenamiento del archivo.
    • Si no es el primer bloque de archivos, agregue el bloque de archivos actual al archivo existente.
    • Si es el primer bloque de archivos, cree un nuevo archivo y escriba los datos del bloque actual.
    • Devuelve un mensaje de éxito, incluida la URL del vídeo subido.
  6. Inicie el servicio: utilice app.listen()el método para iniciar el servidor, escuchando en el puerto especificado (8000 en este caso).

 

ejecutar proyecto

Ahora hemos terminado de codificar el front-end y el back-end. A continuación, debemos ejecutar el proyecto localmente para probar la funcionalidad de carga de archivos.

1. Cree un directorio de carga temporal

upload_tempCree una carpeta llamada .py en la raíz de su proyecto para contener fragmentos de archivos temporales cargados.

2. Inicie el servidor

Ejecute el siguiente comando para iniciar el servidor backend:

node server.js

3. Inicie la parte delantera

Coloque el código de front-end (HTML, CSS y JavaScript) en una carpeta y luego ejecute un servidor HTTP debajo de la carpeta (está bien usar npm e hilo, npm install——npm run dev/yarn——yarn dev) , como usar http-server:

npx http-server

Ahora, visite la página de inicio (generalmente http://localhost:8080) a través del navegador. Seleccione un archivo de video compatible (formato .mp4 o .ogv), haga clic en el botón de carga y verá una barra de progreso de carga que muestra el progreso de la carga. Una vez completada la carga, la página mostrará el mensaje de que la carga se realizó correctamente y mostrará el archivo de video cargado en la página. 

conclusión

En este blog, aprendimos cómo implementar una función de carga de archivos front-end simple y combinamos Node.js y Express para construir un servidor back-end para manejar la carga de archivos. En proyectos reales, puede ampliar y optimizar la función de carga de archivos según sus necesidades. Espero que este blog pueda ayudarlo a comprender los principios básicos y los métodos prácticos de carga de archivos. ¡gracias por leer!

 

 

Si todavía tiene preguntas sobre la carga de archivos front-end o está interesado en otros temas técnicos, bienvenido a unirse al grupo de discusión o preste atención.

 

Supongo que te gusta

Origin blog.csdn.net/weixin_60895836/article/details/131995893
Recomendado
Clasificación