返回

如何解决 Google 云端硬盘 API 并发文件夹创建问题?

java

解决 Google 云端硬盘 API 并发文件夹创建问题

在使用 Google 云端硬盘 API 创建文件夹时,你可能会遇到在多线程环境下创建多个具有相同名称的文件夹的问题。即使将类设置为单例并使用同步方法调用 API,这种行为仍然出乎意料。

根本原因

此问题的根本原因在于并发调用之间的竞态条件。当多个线程同时尝试创建具有相同名称的文件夹时,API 可能会创建多个文件夹,即使已经存在具有相同名称的文件夹。

解决方案

解决此问题的关键是确保在创建新文件夹之前始终检查现有文件夹。你可以通过以下方式实现此目的:

  1. 客户端状态管理: 明确管理客户端状态,使用锁或同步机制来确保在上传过程中状态的完整性。
  2. 优化文件夹查找查询: 使用准确的过滤条件来缩小搜索范围并提高效率。考虑使用缓存机制来存储最近找到的文件夹 ID。
  3. 使用请求批处理: 合并多个 API 调用以提高效率和减少并发问题。
  4. 验证用户身份: 正确验证用户身份以避免 API 产生意外行为。

具体代码示例

根据你提供的代码示例,可以考虑以下修改:

private synchronized void upload(
        final GenerateWaveformPdfEvent event,
        final PresentedWaveformPdfParams params,
        final byte[] pdfRawBytes,
        final IqviaWhoopUser iqviaWhoopUser
    ) throws IOException {
        final int userId = event.getUserId();
        final OffsetDateTime startTime =
            OffsetDateTime.ofInstant(Instant.ofEpochMilli(params.getStartTime()), ZoneOffset.UTC);
        final String iqviaStart = startTime.format(IQVIA_TIMESTAMP);
        final String iqviaWhoopId = iqviaWhoopUser.getIqviaUserId();

        /* get user folder */
        String[] pathList = {"site-1", iqviaWhoopId};

        String folderId = iqviaGoogleDrive;
        for (String folder : pathList) {
            String query =
                "'" + folderId + "' in parents and name='" + folder
                    + "' and mimeType='application/vnd.google-apps.folder' and trashed=false";
            final Drive.Files.List request = googleDriveClient.files().list().setQ(query);

            // Lock to ensure consistency during concurrent access
            synchronized (request) {
                FileList files = request.execute();
                if (files.getFiles().isEmpty()) {
                    final File fileMetadata = new File();
                    fileMetadata.setName(folder);
                    fileMetadata.setMimeType(FOLDER_MIME_TYPE);
                    fileMetadata.setParents(Collections.singletonList(folderId));
                    final File createdFolder = googleDriveClient.files().create(fileMetadata).execute();
                    folderId = createdFolder.getId();
                } else {
                    folderId = files.getFiles().get(0).getId();
                }
            }
        }

        final List<String> parents = new ArrayList<>();
        parents.add(folderId);

        // write the PDF file
        final String iqviaGooglePdfKey = String.format(GOOGLE_IQVIA_PDF_KEY, iqviaWhoopId, iqviaStart);
        final File pdfFileMetadata = new File();
        pdfFileMetadata.setName(iqviaGooglePdfKey);
        pdfFileMetadata.setParents(parents);
        pdfFileMetadata.setMimeType(PDF_MIME_TYPE);

        googleDriveClient.files()
            .create(pdfFileMetadata, new InputStreamContent(PDF_MIME_TYPE, new ByteArrayInputStream(pdfRawBytes)))
            .execute();

        // write the csv file
        final String iqviaGoogleCsvKey = String.format(GOOGLE_IQVIA_CSV_KEY, iqviaWhoopId, iqviaStart);
        String csvContent =
            iqviaWhoopId + "," + iqviaStart + "," + params.getAverageHeartRate() + "," + params.getDetermination();

        final File csvFileMetadata = new File();
        csvFileMetadata.setName(iqviaGoogleCsvKey);
        csvFileMetadata.setParents(parents);
        csvFileMetadata.setMimeType(MediaType.TEXT_PLAIN);
        googleDriveClient.files()
            .create(
                csvFileMetadata,
                new InputStreamContent(
                    MediaType.TEXT_PLAIN,
                    new ByteArrayInputStream(csvContent.getBytes(StandardCharsets.UTF_8))
                )
            )
            .execute();
    }

结论

通过实施这些解决方案,你可以确保在使用 Google 云端硬盘 API 创建文件夹时避免重复文件夹的创建。这将提高效率并保持数据组织良好。

常见问题解答

1. 为什么并发文件夹创建是一个问题?

重复文件夹会造成数据管理混乱、浪费存储空间并影响应用程序性能。

2. 除了文中提到的方法之外,还有什么解决办法?

使用原子操作或分布式锁可以进一步确保文件夹创建的原子性。

3. 如何确保验证用户的身份?

使用 Google OAuth 或服务帐户进行身份验证。

4. 我已经实现了文中提到的解决方案,但仍然遇到重复文件夹问题。我应该怎么做?

检查你的代码是否存在逻辑错误或竞态条件。考虑使用调试工具或日志记录来识别问题根源。

5. 这些解决方案是否适用于 Google 云端硬盘 API 的所有版本?

提供的解决方案适用于 Google 云端硬盘 API 的所有版本。