Java se da cuenta de la función de cargar archivos grandes en fragmentos (tanto en la parte delantera como en la trasera, el código se puede ejecutar directamente después de que la configuración esté inactiva)

problema

El problema resuelto por el proyecto es principalmente java para realizar la función de subir en pedazos. Descripción del problema: El
anfitrión usó un archivo multiparte para subir el archivo de video al servidor en el proyecto reciente de la compañía, y luego guardarlo en la base de datos con fastdfs. Se encuentra que cuando el archivo de video cargado es demasiado grande, el búfer / caché de la memoria del servidor se ocupará muy alto (varios G). Aunque se puede borrar manualmente, aún no puede resolver el problema de la memoria de carga de video desde la causa raíz.

Inserte la descripción de la imagen aquí


Ideas para solucionar el problema

lz gastó 100 puntos en el problema mencionado en csdn: de prisa, pida a java que cargue archivos grandes y tome demasiada solución / idea de jvm

1. mmf, a través del archivo mapeado en memoria, archivo mapeado en memoria para segmentar los datos en mysql u otras bases de datos, no es adecuado, ligeramente
2. FTP en el servidor, y luego usa ftp para implementar el código
3. El front-end implementa el archivo a través de vue-upload La función de carga de segmento, el backend utiliza la función de segmentación propia de fastdfs para lograr el almacenamiento de datos (solución lz)



Problemas resueltos / funciones implementadas

Funciones de front-end : simple-uploader.js (también conocido como Uploader) es una biblioteca de carga que admite múltiples cargas concurrentes, carpetas, arrastrar y soltar, pausar y reanudar, segunda carga, carga en fragmentos, retransmisión automática de errores, retransmisión manual , Progreso, tiempo restante, velocidad de carga y otras características; la biblioteca de carga se basa en la API de archivos HTML5.

Funciones de implementación de back-end : springboot integra fastDfs, implementación de redis, carga de segmento de archivo de ruta local 2. Carga de archivo fastDfs, descarga, carga de segmento



Tecnología de realización de proyectos

Permítanme hablarles sobre las cosas generales que se usan para evitar que su base de datos u otras razones no estén disponibles, lo que lleva a una pérdida de tiempo.
Tecnología utilizada: simple-uploader (front-end) + fastdfs (base de datos) + springboot (marco del proyecto) + Redis

Dirección de origen del proyecto:

  • Interfaz: cargador simple
  • Backend: fastDfs-demo
    Nota: Estoy muy agradecido con el autor que proporcionó el proyecto de código abierto El código del host se cambia en base a dos proyectos, que son códigos fuente.
  • Código propio del propietario:
    enlace: código de extracción de dirección
    : ey36



Efecto logrado

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí



Principio de implementación:

  • Una vez que la interfaz utiliza el complemento de fragmentación, una solicitud se dividirá en varias solicitudes. Varias solicitudes de carga son todas solicitudes fragmentadas. Divida el archivo grande en varias partes pequeñas a la vez. Una vez que los fragmentos se envían al servidor a la vez, es decir, una vez que se completa la carga, se debe enviar una solicitud de combinación al servidor para permitir que el servidor divida varios archivos fragmentados Sintetizar un archivo. Cuando cargamos un archivo grande, el plugin lo fragmentará. La solicitud ajax es la siguiente:
    Inserte la descripción de la imagen aquí

  • Puede ver que se han iniciado varias solicitudes de carga, echemos un vistazo a los parámetros específicos enviados por carga
    Inserte la descripción de la imagen aquí

  • El guid en la primera configuración (content-disposition) y el access_token en la segunda configuración son formData en la configuración del webuploader, es decir, los parámetros pasados ​​al servidor. Las siguientes configuraciones son el contenido del archivo, chunkNumber, chunkSize, currentChunkSize, etc. Entre ellos, totalChunks es el número total de fragmentos y chunkSize es el número actual de fragmentos. 13 en la imagen. Cuando ve una solicitud de carga con un fragmento de 130, significa que esta es la última solicitud de carga

  • Después de la fragmentación, los archivos no se han integrado y los datos se ven así:
    Inserte la descripción de la imagen aquí

  • Verificación de backend
    1 En la devolución de llamada de "Agregar archivo", lea el archivo a través de FileReader, genere MD5 y envíelo al backend
    2.1 Si el backend devuelve directamente el campo "Omitir carga" y la URL del archivo, se omitirá la carga. Esto es segundos Transferencia;
    2.2 Si la información del fragmento se devuelve en segundo plano, se trata de una transferencia que se reanuda. El fondo identificará en cada fragmento si el fragmento se ha cargado, debe juzgar en la devolución de llamada de la verificación de carga del fragmento, si es cierto, omita el fragmento.
    3 Cuando cada segmento se carga correctamente, el fondo devolverá un campo para determinar si es necesario fusionarlo; en la devolución de llamada "upload complete", si este campo es verdadero, debe enviar una solicitud ajax al fondo para solicitar la fusión

Compartir código

El código modificado del proyecto compartido frente al host

Código del núcleo de back-end:

capas de api:

@PostMapping(value = "/fastDfsChunkUpload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Map chunkUpload1(MultipartFileParam multipartFileParam, HttpServletResponse response) {
    
    
        Map<String, String> map = new HashMap<>();
        long chunk = multipartFileParam.getChunkNumber();
        long totalChunk = multipartFileParam.getTotalChunks();
        long chunkSize = multipartFileParam.getChunkSize();
        long historyUpload = (chunk - 1) * chunkSize;
        String md5 = multipartFileParam.getIdentifier();
        MultipartFile file = multipartFileParam.getFile();
        String fileName = FileUtil.extName(file.getOriginalFilename());
        StorePath path = null;
        String groundPath;

        try {
    
    
            if (chunk == 1) {
    
    
                path = appendFileStorageClient.uploadAppenderFile(UpLoadConstant.DEFAULT_GROUP, file.getInputStream(),
                        file.getSize(), fileName);
                if (path == null) {
    
    
                    map.put("result", "上传第一个就错了");
                    response.setStatus(500);
                    return map;
                } else {
    
    
                    redisUtil.setObject(UpLoadConstant.uploadChunkNum + md5, 1, cacheTime);
                    map.put("result", "上传成功");
                }
                groundPath = path.getPath();
                redisUtil.setObject(UpLoadConstant.fastDfsPath + md5, groundPath, cacheTime);

            } else {
    
    
                groundPath = (String) redisUtil.getObject(UpLoadConstant.fastDfsPath + md5);
                appendFileStorageClient.modifyFile(UpLoadConstant.DEFAULT_GROUP, groundPath, file.getInputStream(),
                        file.getSize(), historyUpload);
                Integer chunkNum = (Integer) redisUtil.getObject(UpLoadConstant.uploadChunkNum + md5);
                chunkNum = chunkNum + 1;
                redisUtil.setObject(UpLoadConstant.uploadChunkNum + md5, chunkNum, cacheTime);
            }
            Integer num = (Integer) redisUtil.getObject(UpLoadConstant.uploadChunkNum + md5);
            if (totalChunk == num) {
    
    
                response.setStatus(200);
                map.put("result", "上传成功");
                map.put("path", groundPath);
                redisUtil.del(UpLoadConstant.uploadChunkNum + md5);
                redisUtil.del(UpLoadConstant.fastDfsPath + md5);
            }
        } catch (FdfsIOException | SocketTimeoutException e) {
    
    
            response.setStatus(407);
            map.put("result", "重新发送");
            return map;
        } catch (Exception e) {
    
    
            e.printStackTrace();
            redisUtil.del(UpLoadConstant.uploadChunkNum + md5);
            redisUtil.del(UpLoadConstant.fastDfsPath + md5);
            response.setStatus(500);
            map.put("result", "upload error");
            return map;
        }
        System.out.println("result=" + map.get("result"));
        System.out.println("path=" + map.get("path"));
        return map;
    }

Clase de entidad: MultipartFileParam

package com.dgut.fastdfs.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.web.multipart.MultipartFile;

import java.io.Serializable;

/**
 * @author :陈文浩
 * @date :Created in 2019/12/15 12:27
 * @description:
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class MultipartFileParam implements Serializable {
    
    

    private String taskId;//文件传输任务ID
    private long chunkNumber;//当前为第几分片
    private long chunkSize;//每个分块的大小
    private long totalChunks;//分片总数
    private String identifier;//文件唯一标识
    private MultipartFile file;//分块文件传输对象

}

Herramientas: RedisUtil

package com.dgut.fastdfs.utils;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;


import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {
    
    

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;


    //写入对象
    public boolean setObject(final String key, Object value, Integer expireTime) {
    
    
        try {
    
    

            redisTemplate.opsForValue().set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
    
    
            e.printStackTrace();
            return false;
        }
    }

    //获取对象
    public Object getObject(final String key) {
    
    
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }


    //写入集合
    public boolean setList(final String key, Object value, Integer expireTime) {
    
    
        try {
    
    
            redisTemplate.opsForList().rightPush(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
    
    
            e.printStackTrace();
            return false;
        }
    }

    //获取集合
    public List<Object> getList(final String key) {
    
    
        try {
    
    
            return redisTemplate.opsForList().range(key, 0, -1);
        } catch (Exception e) {
    
    
            e.printStackTrace();
            return null;
        }
    }

    //判断时候存在key
    public boolean hasKey(final String key) {
    
    
        try {
    
    
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return false;
    }

    //删除key
    public void del(final String key) {
    
    
        if (hasKey(key)) {
    
    
            redisTemplate.delete(key);
        }
    }


}

Los principales lugares para modificar el front-end.

  • API para visitar
  • La cantidad de fragmentos cargados al mismo tiempo El
    Inserte la descripción de la imagen aquí
    código del proyecto se cargó en el disco de red de Baidu, puede descargarlo usted mismo

final

Pasé dos horas clasificando los recursos, olvídate de todos después de verlo, no olvides que me gusta, gracias


No seas melancólico en la próxima vida

Supongo que te gusta

Origin blog.csdn.net/qq_42910468/article/details/108607427
Recomendado
Clasificación