实习总结:文件迁移

根据项目需求,将文件从FDFS,SASS迁移到azure。此文章记录了java对三个服务器文件上传下载简单的实现。

@Controller
@RequestMapping("/file")
public class FileFloadMigrationController extends BaseController {

    private static Logger log = LoggerFactory.getLogger(FileServiceImpl.class);
    @Autowired
    @Qualifier("fileUploadServiceImpl")
    private FileService fileService;

    @Autowired
    private FileUploadedToDao fileUploadedToDao;

    @Autowired(required = false)
    protected MongoTemplate mongoTemplate;

    @Autowired
    private FileMapDao fileMapDao;

    @Autowired
    private FileMapService fileMapService;

    @Autowired
    private FileUploadedService fileUploadedService;

    /**
     * 文件迁移
     * 
     * @Title: fileMigration
     * @param state
     *            状态:fail:失败 upload:上传
     * @param module
     *            模块名 module=all:所有模块
     * @param lastModifyTime
     *            最后修改时间:存在该项时证明要导入新增数据
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/filefload/{state}/{module}", method = RequestMethod.GET)
    public @ResponseBody Rest fileMigration(@PathVariable String state, @PathVariable String module, String lastModifyTime) throws Exception {
        new HttpClient().getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(0, false));
        List fileUploadeds = new ArrayList();
        List findFileUploadedTos = new ArrayList();
        Map result = new HashMap();
        // 上传失败后
        if (state.equals("fail")) {
            // 从新上传所有模块中失败数据
            if (module.equals("all")) {
                findFileUploadedTos = getFileUploadsFail();
            } else {
                // 按模块上传
                findFileUploadedTos = getFileUploadsFail(module);
            }

            result = uploadFileFail(findFileUploadedTos);

        } else if (state.equals("upload")) {
            if (module.equals("all")) {
                // 上传所有模块中数据
                fileUploadeds = getFileUploadsNomal();
            } else {
                // 按模块上传数据
                fileUploadeds = getFileUploadsNomal(module);
            }
            // 最后修改时间不为空,需要上传新文件
            // 比如2017-11-09日转移一次后,11日需要在转移。此时lastModifyTime 需要写为2017-11-09
            if (lastModifyTime != null && !lastModifyTime.equals("")) {
                fileUploadeds = getFileUploadTosByLastModify(lastModifyTime);
            }

            result = uploadFileNomal(fileUploadeds);
        }

        return Rest.item(StateCode.STATUS_CODE_SUCCESS, result);
    }

    /**
     * 正常情况下文件迁移,不按照模块。所有模块
     * 
     * @return List
     */
    private List getFileUploadsNomal() {
        List fileUploadeds = new ArrayList();
        Query query = new Query();
        fileUploadeds = mongoTemplate.find(query, FileUploaded.class);
        return fileUploadeds;
    }

    /**
     * 正常情况下文件迁移,按照模块
     * 
     * @Title: getFileUploadsNomal
     * @param module
     *            模块名字
     * @return List
     */
    private List getFileUploadsNomal(String module) {
        List fileUploadeds = new ArrayList();
        Query query = new Query();
        Criteria criteria = Criteria.where("module").is(module);
        query.addCriteria(criteria);
        fileUploadeds = mongoTemplate.find(query, FileUploaded.class);
        return fileUploadeds;
    }

    /**
     * 上传过程中出现失败情况。从新上传。所有模块
     * 
     * @Title: getFileUploadsFail
     * @return List
     */
    private List getFileUploadsFail() {
        List findFileUploadedTos = new ArrayList();
        Query query = new Query();
        Criteria criteria = Criteria.where("isSuccess").is(false);
        query.addCriteria(criteria);
        findFileUploadedTos = mongoTemplate.find(query, FileUploadedTo.class);
        return findFileUploadedTos;
    }

    /**
     * 上传过程中出现失败情况。从新上传。按模块进行上传
     * 
     * @Title: getFileUploadsFail
     * @param module
     *            模块名
     * @return List
     */
    private List getFileUploadsFail(String module) {
        List findFileUploadedTos = new ArrayList();
        Query query = new Query();
        Criteria criteria = Criteria.where("fileUploaded.module").is(module).and("isSuccess").is(false);
        query.addCriteria(criteria);
        findFileUploadedTos = mongoTemplate.find(query, FileUploadedTo.class);
        return findFileUploadedTos;
    }

    /**
     * 上传部分后,出现新增数据。根据文件最后更新时间,进行操作
     * 
     * @Title: getFileUploadTosByLastModify
     * @param lastModifyTime
     *            最后修改时间
     * @return List
     * @throws ParseException 
     */
    private List getFileUploadTosByLastModify(String lastModifyTime) throws ParseException {
        List fileUploadeds = new ArrayList();
        Query query = new Query();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Criteria criteria = Criteria.where("lastModify").gte(sdf.parse(lastModifyTime));
        query.addCriteria(criteria);
        fileUploadeds = mongoTemplate.find(query, FileUploaded.class);
        return fileUploadeds;
    }

    /**
     * 正常情况下上传文件主方法
     * 
     * @Title: uploadFileNomal
     * @param fileUploadeds
     * @return
     * @throws Exception
     */
    private Map uploadFileNomal(List fileUploadeds) throws Exception {
        Map map = new HashMap();
        List fileUploadedTos = new ArrayList();
        if (fileUploadeds == null || fileUploadeds.size() == 0) {
            return map;
        }
        System.out.println("共有文件:" + fileUploadeds.size() + "条");
        long startTime = System.currentTimeMillis();
        // 将表中的数据复制到扩展表中(将文件所有信息复制、设置org为要迁移文件的org、设置迁移失败(false))
        for (FileUploaded fileUploaded : fileUploadeds) {
            FileUploadedTo fileUploadedTo = new FileUploadedTo();
            fileUploadedTo.setFileUploaded(fileUploaded);
            fileUploadedTo.setOrg(fileUploaded.getOrg());
            fileUploadedTo.setSuccess(false);
            fileUploadedTos.add(fileUploadedTo);
        }
        // 保存复制表
        fileUploadedToDao.save(fileUploadedTos);

        uploadFileCurrency(fileUploadedTos);

        long endTime = System.currentTimeMillis();
        System.out.println("程序运行时间: " + (endTime - startTime) + "ms");
        return map;
    }

    /**
     * 上传失败后重新上传主方法
     * 
     * @Title: uploadFileFail
     * @param fileUploadedTos
     * @return
     */
    private Map uploadFileFail(List fileUploadedTos) throws Exception {
        Map map = new HashMap();
        
        uploadFileCurrency(fileUploadedTos);
        
        return map;
    }

    /**
     * 上传文件通用方法
     * 
     * @Title: uploadFileCurrency
     * @param fileUploadedTos
     */

    private void uploadFileCurrency(List fileUploadedTos) {
        int i = 0;
        for (FileUploadedTo fileUploadedTo : fileUploadedTos) {
            String path = fileUploadedTo.getFileUploaded().getPath();
            
            if (path != null && path.indexOf("group") != -1) {
                byte fileByte[] = fileService.getFileBytes(path);
                if (fileByte == null) {
                    System.out.println("第" + i++ + "个 fileByte不存在,path是" + path);
                } else {
                    if (path.indexOf(".pdf.") == -1) {// 不是从文件
                        uploadFile(fileUploadedTo, fileByte);
                    } else {// 是从文件
                        uploadSlaveFile(fileByte, "file.pdf", fileUploadedTo);
                    }
                }
            }
        }
    }

    /**
     * 上传文件到分布式系统,并写入文件 文件限制大小 上传的文件可以直接通过id使用特定接口访问 上传失败return null
     * 
     * @param fileBytes
     *            文件
     * @param name
     *            文件名称
     * @param module
     *            模块标示
     * @return
     */
    private void uploadFile(FileUploadedTo fileUploadedTo, byte[] fileBytes) {
        // 获取fileUpload
        FileUploaded fileUploaded = fileUploadedTo.getFileUploaded();
        // 获取模板名
        String module = fileUploaded.getModule();
        // 获取当前文件信息内的org
        String org = fileUploaded.getOrg();
        // 获取文件名
        String name = fileUploaded.getName();
        // 获取文件ID
        String fileId = fileUploaded.getId();
        if (!isLegal(module)) {
            log.error("module ilegal: 模块名称非法");
        }
        try {
            // 长度小于3报错
            if (org.length() < 4) {
                org = "org" + org;
            }
            CloudBlockBlob blob = BlobClient.uploadFile(fileBytes, org, module, name);
            // 如果上传成功会返回一个Blob对象(不为空)
            // 那么此时就将上传后的新路径以及是否上传成功状态进行更改。
            if (blob != null) {
                // 设置更新成功
                fileUploadedTo.setSuccess(true);
                // 设置新路径
                String newPath = BlobClient.getPath(org, blob.getName());
                fileUploadedTo.setNewPath(newPath);
                fileUploadedToDao.update(fileUploadedTo);

                // 更新fileUpload路径。
                fileUploaded.setPath(newPath);
                mongoTemplate.save(fileUploaded);

                // 更新filemap路径
                FileMap fileMap = findByFileId(fileId);
                if (fileMap != null) {
                    fileMap.setFilePath(newPath);
                    fileMapService.update(fileMap);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 根据fileId 获取 fileMap。
     * 
     * @Title: findByFileId
     * @param id
     *            文件ID
     * @return
     * @throws
     */
    private FileMap findByFileId(String id) {
        Query query = new Query(Criteria.where("fileId").is(id));
        return fileMapDao.findOne(query);
    }

    /**
     * 校验模块是否合法
* 模块名在properties中存在即合法 * * @param module * 模块名称 * @return 合法返回true ,非法返回false */ private boolean isLegal(String module) { if (module.indexOf('.') >= 0) { return false; } String v = PropertiesUtils.getString("file.path." + module); if (v == null || v.length() < 1) { return false; } else { return true; } } /** * 上传从文件主方法 * * @Title: uploadSlaveFile * @param fileBytes * 从文件字节大小 * @param prefix_name * 从文件前缀 * @param fileUploadedTo * fileUploaded 复制表 */ private void uploadSlaveFile(byte[] fileBytes, String prefix_name, FileUploadedTo fileUploadedTo) { String org = fileUploadedTo.getFileUploaded().getOrg(); FileUploaded master = fileUploadedTo.getFileUploaded(); String preName = master.getName().substring(0, master.getName().lastIndexOf(".")); prefix_name = BlobClient.getPath(preName.concat("_cut"), preName.concat(".pdf")); System.out.println(prefix_name); try { CloudBlockBlob blob = BlobClient.uploadFile(fileBytes, org, master.getModule(), prefix_name); if (blob != null) { fileUploadedTo.setSuccess(true); } fileUploadedTo.setNewPath(BlobClient.getPath(org, blob.getName())); } catch (InvalidKeyException | URISyntaxException | StorageException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } fileUploadedToDao.update(fileUploadedTo); } }

上面的类首先获取数据库中的文件,然后根据文件的路径获取文件的字节。如果路径符合规范且字节不为空,那么就将该文件上传。下面的方法是上传到Azure的方法。

Azure Storage 是微软 Azure 云提供的云端存储解决方案,当前支持的存储类型有 Blob、Queue、File 和 Table。

Azure Blob Storage 是用来存放大量的像文本、图片、视频等非结构化数据的存储服务。我们可以在任何地方通过互联网协议 http 或者 https 访问 Blob Storage。简单说,就是把文件放在云上,给它一个 URL,通过这个 URL 来访问文件。这就涉及到一个问题:如何控制访问权限?答案是我们可以根据自己的需要,设置 Blob 对象是只能被自己访问,还是可以被所有人访问。

下面是 Blog Storage 典型的应用场景:

  • 存储图片和文档,这些文件可以直接通过浏览器访问。
  • 支持分布式访问,主要用于 cdn。
  • 提供视频、音频流。
  • 存储基本的文件备份和归档文件。
    static final String connectionString = String.format(
            "DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=core.chinacloudapi.cn",
            storageAccountName, storageAccountKey);
    /**
     * 通过容器的方式实现上传二进制文件
     * @throws InvalidKeyException 
     * @time:2017年9月22日 上午10:40:19
     */
    public static CloudBlockBlob uploadFile(byte[] fileBytes,String org,String module,String fileName) throws URISyntaxException, StorageException, IOException, InvalidKeyException{
        // CloudStorageAccount 类表示一个 Azure Storage Account,我们需要先创建它的实例,才能访问属于它的资源。
        // 注意连接字符串中的xxx和yyy,分别对应Access keys中的Storage account name 和 key。
        CloudStorageAccount storageAccount = CloudStorageAccount.parse(connectionString);

        // Create the blob client object.
        CloudBlobClient blobClient = storageAccount.createCloudBlobClient();
        CloudBlobContainer container = blobClient.getContainerReference(org);
        container.createIfNotExists(); // 创建一个容器(如果该容器不存在)
        // 保存到azure上的文件的名称,由于azure服务器对重名文件进行覆盖处理,
        // 上传时对文件名称进行唯一标识处理,缺点是在文件服务器看不到文件的真识名称了。
        UUID token = UUID.randomUUID();
        int postFixIndex = fileName.lastIndexOf(".");
        String fileFullName = "";
        String postFix = "";
        String startName = "";
        if (postFixIndex > 0) {
            postFix = fileName.substring(postFixIndex + 1);
            startName = fileName.substring(0, postFixIndex);
        }else {
            startName = fileName;
        }

        /*azure服务器存储相同文件名的文件会覆盖之前的,因此需要对文件名进行处理
        存储到azure上的文件名格试为:原文件名+"_"+uuid+扩展名
        */
        fileFullName = startName.concat("_").concat(token.toString()).concat(".").concat(postFix);


        /*getPath(module,fileFullName) 为放在 container 中的 Blob 的连接路径。
          getBlockBlobReference 方法获得一个 Block 类型的 Blob 对象的引用。
          您可以根据应用的需要,分别调用 getBlobReference,getAppendBlobReference 或 getPageBlobReference 来创建不同类型的 Blob 对象。
        */
        CloudBlockBlob blob = container.getBlockBlobReference(getPath(module,fileFullName));
        try
        {
            InputStream stream = new ByteArrayInputStream(fileBytes);
          //blob.upload(stream, (long) fileBytes.length);
            PutBlock(blob,stream);
        }
        catch (StorageException e)
        {
            return null;
        }
      return blob;
    }

大文件分块上传:

  /**
    *文件过大,分块上传
    * @Title: PutBlock
    * @param blob
    * @param stream
    * @throws StorageException
    * @throws IOException
     */
@SuppressWarnings({ "unchecked", "rawtypes" })
    public static void PutBlock(CloudBlockBlob blob,InputStream stream) throws StorageException, IOException  
    {  
        Iterable blockList = new ArrayList();
        int len = stream.available();
        int bytesRead = 0;
        int cur_read_len = STEP_LENGTH;
        byte[] b = null;
        int index = 0;
        if (len <= STEP_LENGTH) {// 如果文件没有超过设定长度,一次上传上即可
            blob.upload(stream, (long) len);
        } else {// 如果文件太大,分批上传
            while (true) {
                if (len - bytesRead > STEP_LENGTH) {
                    cur_read_len = STEP_LENGTH;
                } else {
                    cur_read_len = len - bytesRead;
                }
                b = new byte[cur_read_len + 1];
                int bytesReadLength = 0;
                bytesReadLength = stream.read(b, 0, cur_read_len);
                if (bytesReadLength == -1){// end of InputStream
                    blob.commitBlockList(blockList);
                    break;
                }
                bytesRead += bytesReadLength;
                if (bytesRead <= len) {
                    try {
                        String blockId = Base64.getEncoder().encodeToString(String.format("%08d",index).getBytes(StandardCharsets.UTF_8));
                        blob.uploadBlock(blockId, new ByteArrayInputStream(b),Long.valueOf(bytesReadLength));
                        ((AbstractCollection) blockList).add(new BlockEntry(blockId));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    
                } 
                index++;
            }
        }
      
    }

你可能感兴趣的:(实习总结:文件迁移)