Java负载均衡服务器实现上传文件同步

负载服务器Z,应用服务器A 和B ,从A上传的附件,如何在B上下载下来?

这个问题我的解决思路如下(后来被一个装逼的面试官给批评了这种做法,不过我瞧不起他)

服务器A、B 上传附件的时候,将这个附件备份到服务器Z ,当A、B下载文件的时候,首先会在自己服务器的目录下寻找,如果找不到,就会从服务器Z 上下载一份到当前服务器。

服务器之间的文件备份通过sftp,参考:https://www.jb51.net/article/196008.htm(下文中的SftpCustom 类就是这个链接里的 “SFTP上传下载文件例子” 中的类)

这里主要介绍一下重写上传、下载的方法时应该添加的代码

上传文件,异步操作

 new Thread(() -> {

  SftpCustom fu = new SftpCustom();
  fu.upload(file.getAbsolutePath(), getFileName(fileDescr));
  fu.closeChannel();
}).start();

下载文件,先从当前服务器寻找

String tmpPath = roots[0].getPath() + '/' + getFileName(fileDescr);
File file2 = new File(tmpPath);
if (file2.exists()) {
  return FileUtils.openInputStream(file2);
}

SftpCustom fu = new SftpCustom();
fu.download(getFileName(fileDescr), tmpPath);
file2 = new File(tmpPath);
inputStream = FileUtils.openInputStream(file2);
fu.closeChannel();
return inputStream;

cuba 框架中重写上传文件类FileStorage.java 的代码如下:

package com.haulmont.cuba.core.app.custom;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.haulmont.cuba.core.app.FileStorageAPI;
import com.haulmont.cuba.core.app.ServerConfig;
import com.haulmont.cuba.core.entity.FileDescriptor;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.SecurityContext;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.haulmont.bali.util.Preconditions.checkNotNullArgument;

public class FileStorage implements FileStorageAPI {

  private final Logger log = LoggerFactory.getLogger(FileStorage.class);

  @Inject
  protected UserSessionSource userSessionSource;

  @Inject
  protected TimeSource timeSource;

  @Inject
  protected Configuration configuration;

  protected boolean isImmutableFileStorage;

  protected ExecutorService writeExecutor = Executors.newFixedThreadPool(5,
      new ThreadFactoryBuilder().setNameFormat("FileStorageWriter-%d").build());

  protected volatile File[] storageRoots;

  @PostConstruct
  public void init() {
    this.isImmutableFileStorage = configuration.getConfig(ServerConfig.class).getImmutableFileStorage();
  }

  /**
   * INTERNAL. Don't use in application code.
   */
  public File[] getStorageRoots() {
    if (storageRoots == null) {
      String conf = configuration.getConfig(ServerConfig.class).getFileStorageDir();
      if (StringUtils.isBlank(conf)) {
        String dataDir = configuration.getConfig(GlobalConfig.class).getDataDir();
        File dir = new File(dataDir, "filestorage");
        dir.mkdirs();
        storageRoots = new File[]{dir};
      } else {
        List<File> list = new ArrayList<>();
        for (String str : conf.split(",")) {
          str = str.trim();
          if (!StringUtils.isEmpty(str)) {
            File file = new File(str);
            if (!list.contains(file))
              list.add(file);
          }
        }
        storageRoots = list.toArray(new File[list.size()]);
      }
    }
    return storageRoots;
  }

  @Override
  public long saveStream(final FileDescriptor fileDescr, final InputStream inputStream) throws FileStorageException {
    checkFileDescriptor(fileDescr);

    File[] roots = getStorageRoots();

    // Store to primary storage

    checkStorageDefined(roots, fileDescr);
    checkPrimaryStorageAccessible(roots, fileDescr);

    File dir = getStorageDir(roots[0], fileDescr);
    dir.mkdirs();
    checkDirectoryExists(dir);

    final File file = new File(dir, getFileName(fileDescr));
    checkFileExists(file);

    long size = 0;
    OutputStream os = null;
    try {
      os = FileUtils.openOutputStream(file);
      size = IOUtils.copyLarge(inputStream, os);
      os.flush();
      writeLog(file, false);

      new Thread(() -> {
        SftpCustom fu = new SftpCustom();
        fu.upload(file.getAbsolutePath(), getFileName(fileDescr));
        fu.closeChannel();
      }).start();

    } catch (IOException e) {
      IOUtils.closeQuietly(os);
      FileUtils.deleteQuietly(file);

      throw new FileStorageException(FileStorageException.Type.IO_EXCEPTION, file.getAbsolutePath(), e);
    } finally {
      IOUtils.closeQuietly(os);
    }

    // Copy file to secondary storages asynchronously

    final SecurityContext securityContext = AppContext.getSecurityContext();
    for (int i = 1; i < roots.length; i++) {
      if (!roots[i].exists()) {
        log.error("Error saving {} into {} : directory doesn't exist", fileDescr, roots[i]);
        continue;
      }

      File copyDir = getStorageDir(roots[i], fileDescr);
      final File fileCopy = new File(copyDir, getFileName(fileDescr));

      writeExecutor.submit(new Runnable() {
        @Override
        public void run() {
          try {
            AppContext.setSecurityContext(securityContext);
            FileUtils.copyFile(file, fileCopy, true);
            writeLog(fileCopy, false);
          } catch (Exception e) {
            log.error("Error saving {} into {} : {}", fileDescr, fileCopy.getAbsolutePath(), e.getMessage());
          } finally {
            AppContext.setSecurityContext(null);
          }
        }
      });
    }

    return size;
  }

  protected void checkFileExists(File file) throws FileStorageException {
    if (file.exists() && isImmutableFileStorage)
      throw new FileStorageException(FileStorageException.Type.FILE_ALREADY_EXISTS, file.getAbsolutePath());
  }

  protected void checkDirectoryExists(File dir) throws FileStorageException {
    if (!dir.exists())
      throw new FileStorageException(FileStorageException.Type.STORAGE_INACCESSIBLE, dir.getAbsolutePath());
  }

  protected void checkPrimaryStorageAccessible(File[] roots, FileDescriptor fileDescr) throws FileStorageException {
    if (!roots[0].exists()) {
      log.error("Inaccessible primary storage at {}", roots[0]);
      throw new FileStorageException(FileStorageException.Type.STORAGE_INACCESSIBLE, fileDescr.getId().toString());
    }
  }

  protected void checkStorageDefined(File[] roots, FileDescriptor fileDescr) throws FileStorageException {
    if (roots.length == 0) {
      log.error("No storage directories defined");
      throw new FileStorageException(FileStorageException.Type.STORAGE_INACCESSIBLE, fileDescr.getId().toString());
    }
  }

  @Override
  public void saveFile(final FileDescriptor fileDescr, final byte[] data) throws FileStorageException {
    checkNotNullArgument(data, "File content is null");
    saveStream(fileDescr, new ByteArrayInputStream(data));
  }

  protected synchronized void writeLog(File file, boolean remove) {
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    StringBuilder sb = new StringBuilder();
    sb.append(df.format(timeSource.currentTimestamp())).append(" ");
    sb.append("[").append(userSessionSource.getUserSession().getUser()).append("] ");
    sb.append(remove ? "REMOVE" : "CREATE").append(" ");
    sb.append("\"").append(file.getAbsolutePath()).append("\"\n");

    File rootDir;
    try {
      rootDir = file.getParentFile().getParentFile().getParentFile().getParentFile();
    } catch (NullPointerException e) {
      log.error("Unable to write log: invalid file storage structure", e);
      return;
    }
    File logFile = new File(rootDir, "storage.log");
    try {
      try (FileOutputStream fos = new FileOutputStream(logFile, true)) {
        IOUtils.write(sb.toString(), fos, StandardCharsets.UTF_8.name());
      }
    } catch (IOException e) {
      log.error("Unable to write log", e);
    }
  }

  @Override
  public void removeFile(FileDescriptor fileDescr) throws FileStorageException {
    checkFileDescriptor(fileDescr);

    File[] roots = getStorageRoots();
    if (roots.length == 0) {
      log.error("No storage directories defined");
      return;
    }

    for (File root : roots) {
      File dir = getStorageDir(root, fileDescr);
      File file = new File(dir, getFileName(fileDescr));
      if (file.exists()) {
        if (!file.delete()) {
          throw new FileStorageException(FileStorageException.Type.IO_EXCEPTION, "Unable to delete file " + file.getAbsolutePath());
        } else {
          writeLog(file, true);
        }
      }
    }
  }

  protected void checkFileDescriptor(FileDescriptor fd) {
    if (fd == null || fd.getCreateDate() == null) {
      throw new IllegalArgumentException("A FileDescriptor instance with populated 'createDate' attribute must be provided");
    }
  }

  @Override
  public InputStream openStream(FileDescriptor fileDescr) throws FileStorageException {
    checkFileDescriptor(fileDescr);

    File[] roots = getStorageRoots();
    if (roots.length == 0) {
      log.error("No storage directories available");
      throw new FileStorageException(FileStorageException.Type.FILE_NOT_FOUND, fileDescr.getId().toString());
    }

    InputStream inputStream = null;
    for (File root : roots) {
      File dir = getStorageDir(root, fileDescr);

      File file = new File(dir, getFileName(fileDescr));
      if (!file.exists()) {
        log.error("File " + file + " not found");
        continue;
      }

      try {
        inputStream = FileUtils.openInputStream(file);
        break;
      } catch (IOException e) {
        log.error("Error opening input stream for " + file, e);
      }
    }
    if (inputStream != null) {
      return inputStream;
    } else {
      try {
        String tmpPath = roots[0].getPath() + '/' + getFileName(fileDescr);
        File file2 = new File(tmpPath);
        if (file2.exists()) {
          return FileUtils.openInputStream(file2);
        }

        SftpCustom fu = new SftpCustom();
        fu.download(getFileName(fileDescr), tmpPath);
        file2 = new File(tmpPath);
        inputStream = FileUtils.openInputStream(file2);
        fu.closeChannel();
        return inputStream;
      } catch (Exception e) {
        throw new FileStorageException(FileStorageException.Type.FILE_NOT_FOUND, fileDescr.getId().toString());
      }
    }
  }

  @Override
  public byte[] loadFile(FileDescriptor fileDescr) throws FileStorageException {
    InputStream inputStream = openStream(fileDescr);
    try {
      return IOUtils.toByteArray(inputStream);
    } catch (IOException e) {
      throw new FileStorageException(FileStorageException.Type.IO_EXCEPTION, fileDescr.getId().toString(), e);
    } finally {
      IOUtils.closeQuietly(inputStream);
    }
  }

  @Override
  public boolean fileExists(FileDescriptor fileDescr) {
    checkFileDescriptor(fileDescr);

    File[] roots = getStorageRoots();
    for (File root : roots) {
      File dir = getStorageDir(root, fileDescr);
      File file = new File(dir, getFileName(fileDescr));
      if (file.exists()) {
        return true;
      }
    }
    return false;
  }

  /**
   * INTERNAL. Don't use in application code.
   */
  public File getStorageDir(File rootDir, FileDescriptor fileDescriptor) {
    checkNotNullArgument(rootDir);
    checkNotNullArgument(fileDescriptor);

    Calendar cal = Calendar.getInstance();
    cal.setTime(fileDescriptor.getCreateDate());
    int year = cal.get(Calendar.YEAR);
    int month = cal.get(Calendar.MONTH) + 1;
    int day = cal.get(Calendar.DAY_OF_MONTH);

    return new File(rootDir, year + "/"
        + StringUtils.leftPad(String.valueOf(month), 2, '0') + "/"
        + StringUtils.leftPad(String.valueOf(day), 2, '0'));
  }

  public static String getFileName(FileDescriptor fileDescriptor) {
    return fileDescriptor.getId().toString() + "." + fileDescriptor.getExtension();
  }

  @PreDestroy
  protected void stopWriteExecutor() {
    writeExecutor.shutdown();
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • ASP.NET WebAPi(selfhost)实现文件同步或异步上传

    前言 前面我们讲过利用AngularJs上传到WebAPi中进行处理,同时我们在MVC系列中讲过文件上传,本文结合MVC+WebAPi来进行文件的同步或者异步上传,顺便回顾下css和js,MVC作为客户端,而WebAPi利用不依赖于IIS的selfhost模式作为服务端来接收客户端的文件且其过程用Ajax来实现,下面我们一起来看看. 同步上传 多余的话不用讲,我们直接看页面. <div class="container"> <div> @if (ViewBag.

  • Java加权负载均衡策略实现过程解析

    加权轮询 后端集群每台机器都分配一个权重,权重高得会承担更多的流量,相反权重低的分配的流量也会少,这种策略允许后端集群机器配置差异化 java实现 import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.springframework.s

  • 详解Java实现负载均衡的几种算法代码

    本篇文章主要介绍Java实现负载均衡的几种算法,具体如下: 轮询: package class2.zookeeper.loadbalance; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * 負載均衡算法,輪詢法 * @author guoy * */ public class TestRoundRobin { static Map<St

  • Java使用Gateway自定义负载均衡过滤器

    背景 最近项目中需要上传视频文件,由于视频文件可能会比较大,但是我们应用服务器tomcat设置单次只支持的100M,因此决定开发一个分片上传接口. 把大文件分成若干个小文件上传.所有文件上传完成后通过唯一标示进行合并文件. 我们的开发人员很快完成了开发,并在单元测试中表现无误.上传代码到测试环境,喔嚯!!!出错了. 经过一段时间的辛苦排查终于发现问题,测试环境多实例,分片上传的接口会被路由到不同的实例,导致上传后的分片文件在不同的机器,那么也就无法被合并. 知道了原因就好解决,经过一系列的过程最

  • php使用ftp远程上传文件类(完美解决主从文件同步问题的方法)

    php使用ftp实现文件上传代码片段: <?php /** * ftp上传文件类 */ class Ftp { /** * 测试服务器 * * @var array */ private $testServer = array( 'host' => 'ip', 'port' => 21, 'user' => 'userName', 'pwd' => 'password' ); /** * 打开并登录服务器 * * @param string $flag 服务器标识test *

  • Java Grpc实例创建负载均衡详解

    Grpc是googe开发的,是一款语言中立.平台中立.开源的远程过程调用(RPC)系统.新公司的项目服务之间的调用使用的Grpc来实现服务间的调用,这边一开始接到的工作内容是基于Nginx实现Grpc服务端的负载均衡.Nginx的1.13及以上版本是支持grpc的反向代理和负载均衡的.但是公司的nginx服务器的版本是1.10的,所以没办法直接使用grpc的代理.只能使用更底层的tcp层的负载均衡.最终服务跑起来是感觉挺简单的,但是nginx的基础太差,所以过程有点曲折.还是记录下吧. 文章分两

  • Bootstrap fileinput 上传新文件移除时触发服务器同步删除的配置

    在Bootstrap fileinput中移除预览文件时可以通过配置initialPreviewConfig: [ { url:'deletefile',key:fileid } ] 来同步删除服务器上的文件和记录.但新上传的文件则需要其他方式来同步删除服务器记录. 在配置中遇到的一些问题,记录一下. fileinput在文件上传成功后会触发'fileuploaded'事件,移除图片后会触发'filesuccessremove'事件. 其中在fileuploaded中参数previewId是形如

  • Java负载均衡服务器实现上传文件同步

    负载服务器Z,应用服务器A 和B ,从A上传的附件,如何在B上下载下来? 这个问题我的解决思路如下(后来被一个装逼的面试官给批评了这种做法,不过我瞧不起他) 服务器A.B 上传附件的时候,将这个附件备份到服务器Z ,当A.B下载文件的时候,首先会在自己服务器的目录下寻找,如果找不到,就会从服务器Z 上下载一份到当前服务器. 服务器之间的文件备份通过sftp,参考:https://www.jb51.net/article/196008.htm(下文中的SftpCustom 类就是这个链接里的 "S

  • Java多线程实现FTP批量上传文件

    本文实例为大家分享了Java多线程实现FTP批量上传文件的具体代码,供大家参考,具体内容如下 1.构建FTP客户端 package cn.com.pingtech.common.ftp; import lombok.extern.slf4j.Slf4j; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; import java.io.*; import java.net

  • Java原生服务器接收上传文件 不使用MultipartFile类

    由于工作中 使用 MultipartFile 与现有的一些上传文件组件冲突 所以使用其他的接收上传文件的方法. 首先我把 MultipartFile 类的配置文件注释掉. <!-- <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaul

  • FTP服务器详解之监控ftp服务器、上传文件到ftp服务器、ftp文件监控的方法

    现在FTP文件服务器的使用极为普遍,可以方便地将文件实时存储在FTP文件服务器上,那么如何搭建FTP文件服务器呢,以及如何监控FTP文件服务器文件访问操作日志情况呢?详细如下: 第1页:FTP服务器的作用 FTP服务器(File Transfer Protocol Server)是在互联网上提供文件存储和访问服务的计算机,它们依照FTP协议提供服务.FTP服务器常常被用来进行文件共享和传输,是互联网领域必不可少的一环. FTP服务器的作用 FTP服务器是为了解决文件传输障碍问题而产生的.那么FT

  • 详解python上传文件和字符到PHP服务器

    很多朋友在留言区询问关于python上传文件和字符到服务器的问题,现编针对这个给大家整理了一个解决办法. 上传简单的字符串 def send_str_server(self): payload = {'key1': 'value1', 'key2': 'value2'} r = requests.post("http://httpbin.org/post", data=payload) 介绍:payload 为键值对形式的数据,在服务器的数据的显示为 key1=value1&k

  • java使用ajax完成上传文件

    本文实例为大家分享了java使用ajax完成上传文件的具体代码,供大家参考,具体内容如下 使用ajax上传文件最重要的就是实例化FormData来进行上传文件: 1.html代码: <div class="kuang">     <p>文件上传</p>     <div class="san3">         <input type="file" name="fujianID&q

  • Java框架Struts2实现图片上传功能

    Struts 2 框架为处理文件上传提供了内置支持,它使用"在 HTML 中基于表单的文件上传".当上传一个文件时,它通常会被存储在一个临时目录中,而且它们应该由 Action 类进行处理或移动到一个永久的目录,用来确保数据不丢失.服务器在恰当的位置可能有一个安全策略,它会禁止你写到除了临时目录以外的目录,而且这个目录属于你的web应用应用程序. 通过预定义的名为文件上传的拦截器,Struts 的文件上传是可能的,这个拦截器在 org.apache.struts2.intercepto

  • HipChat上传文件报未知错误的原因分析及解决方案

    HipChat的功能类似于Campfire.Sazneo等在线协同工具,并且和Yammer以及Salesforce的Chatter等企业社交平台有一定相似之处.你可以为单个项目或者小组搭建自有的聊天室,也可以很方便的发起一对一聊天.这套 IM 系统还整合了团队文件管理和分享,拖拽就能完成保存操作. 前言 HipChat是Atlassian公司的一款团队协作即时通讯工具,服务端为Linux(官方给的服务端就是一个虚拟机),在Windows.Linux.Android.IOS.Mac等平台都有客户端

  • Jsp+Servlet实现文件上传下载 删除上传文件(三)

    接着上一篇讲:Jsp+Servlet实现文件上传下载(二)--文件列表展示 本章来实现一下删除已上传文件,同时优化了一下第一章中的代码. 废话少说,上代码得意 1.调整列表页面list.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/j

  • Java使用SFTP上传文件到服务器的简单使用

    最近用到SFTP上传文件查找了一些资料后自己做了一点总结,方便以后的查询.具体代码如下所示: /** * 将文件上传到服务器 * * @param filePath * 文件路径 * @param channelSftp * channelSftp对象 * @return */ public static boolean uploadFile(String filePath, ChannelSftp channelSftp) { OutputStream outstream = null; In

随机推荐