
# SDK是否支持批量上传、下载或复制对象？(Java SDK)
SDK暂未提供专门的批量上传、下载或复制对象接口，但您可以通过组合现有接口自行封装实现，请参考如下步骤操作：
1. 列举出所有待上传、下载或者复制的对象。可参考[列举对象](https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0803.html)章节，列举需上传、下载或复制的对象。
2. 对列举出的对象调用单个对象的上传（上传对象）、下载（下载对象）或复制（复制对象）接口。
以批量上传对象为例，示例代码如下：
```
import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class BatchUpload001 {
    private List<File> fileList = new ArrayList<>();
    public static void main(String[] args) {
        // 您可以通过环境变量获取访问密钥AK/SK，也可以使用其他外部引入方式传入。如果使用硬编码可能会存在泄露风险。
        // 您可以登录访问管理控制台获取访问密钥AK/SK
        String ak = System.getenv("ACCESS_KEY_ID");
        String sk = System.getenv("SECRET_ACCESS_KEY_ID");
        // 【可选】如果使用临时AK/SK和SecurityToken访问OBS，同样建议您尽量避免使用硬编码，以降低信息泄露风险。
        // 您可以通过环境变量获取访问密钥AK/SK/SecurityToken，也可以使用其他外部引入方式传入。
        // String securityToken = System.getenv("SECURITY_TOKEN");
        // endpoint填写桶所在的endpoint, 此处以华北-北京四为例，其他地区请按实际情况填写。
        String endPoint = "https://obs.cn-north-4.myhuaweicloud.com";
        // 您可以通过环境变量获取endPoint，也可以使用其他外部引入方式传入。
        //String endPoint = System.getenv("ENDPOINT");
        
        // 创建ObsClient实例
        // 使用永久AK/SK初始化客户端
        ObsClient obsClient = new ObsClient(ak, sk, endPoint);
        // 使用临时AK/SK和SecurityToken初始化客户端
        // ObsClient obsClient = new ObsClient(ak, sk, securityToken, endPoint);
        try {
            String bucketName = "examplebucket";
            // 定义桶内对象的前缀
            String objectPre = "object/";
            // 待上传的文件夹
            String localDirPath = "localfile";
            // 扫描文件夹下所有文件和空目录
            File localDir = new File(localDirPath);
            if (!localDir.exists() || !localDir.isDirectory()) {
                System.out.println("本地文件夹不存在: " + localDirPath);
                return;
            }
            BatchUpload001 uploader = new BatchUpload001();
            uploader.listFiles(localDir);
            if (uploader.fileList.isEmpty()) {
                System.out.println("未找到待上传的文件");
                return;
            }
            System.out.println("待上传对象数量: " + uploader.fileList.size());
            // 初始化线程池
            int threadCount = 20;
            ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
            CountDownLatch latch = new CountDownLatch(uploader.fileList.size());
            AtomicInteger successCount = new AtomicInteger(0);
            AtomicInteger failCount = new AtomicInteger(0);
            // 执行并发上传
            for (File f : uploader.fileList) {
                executorService.execute(() -> {
                    try {
                        String relativePath = f.getPath().substring(localDirPath.length() + 1);
                        // 将Windows路径分隔符替换为OBS对象键的分隔符
                        String remoteObjectKey = objectPre + relativePath.replace("\\", "/");
                        if (f.isDirectory()) {
                            // 如果是空文件夹，则在桶内创建对应的空文件夹对象
                            remoteObjectKey += "/";
                            obsClient.putObject(bucketName, remoteObjectKey, new ByteArrayInputStream(new byte[0]));
                        } else {
                            obsClient.putObject(bucketName, remoteObjectKey, f);
                        }
                        successCount.incrementAndGet();
                        System.out.println("上传成功: " + remoteObjectKey);
                    } catch (ObsException e) {
                        failCount.incrementAndGet();
                        System.out.println("上传失败: " + f.getPath()
                                + ", HTTP Code: " + e.getResponseCode()
                                + ", Error Code: " + e.getErrorCode()
                                + ", Error Message: " + e.getErrorMessage());
                    } catch (Exception e) {
                        failCount.incrementAndGet();
                        System.out.println("上传失败: " + f.getPath() + ", " + e.getMessage());
                    } finally {
                        latch.countDown();
                    }
                });
            }
            // 等待上传完成
            executorService.shutdown();
            latch.await(30, TimeUnit.MINUTES);
            System.out.println("上传完成, 成功: " + successCount.get() + ", 失败: " + failCount.get());
        } catch (ObsException e) {
            System.out.println("创建ObsClient失败");
            System.out.println("HTTP Code: " + e.getResponseCode());
            System.out.println("Error Code: " + e.getErrorCode());
            System.out.println("Error Message: " + e.getErrorMessage());
        } catch (Exception e) {
            System.out.println("上传异常: " + e.getMessage());
            e.printStackTrace();
        }
    }
    // 递归扫描文件夹下所有文件和空目录
    private void listFiles(File file) {
        File[] children = file.listFiles();
        if (children == null) {
            return;
        }
        if (children.length == 0) {
            // 空文件夹也需要上传，将其添加到列表中
            fileList.add(file);
            return;
        }
        for (File f : children) {
            if (f.isDirectory()) {
                listFiles(f);
            } else if (f.isFile()) {
                fileList.add(f);
            }
        }
    }
}
```
![](https://support.huaweicloud.com/sdk-java-devg-obs/public_sys-resources/note_3.0-zh-cn.png)
您可以使用多线程并发执行上传/下载/复制操作，以提高效率。
