Intégration d'upload d'objets vers Alibaba Cloud OSS avec le SDK officiel

Optimisation du téléversement de fichiers avec Alibaba Cloud OSS

Le traitement du téléversement de fichiers, en particulier pour les fichiers volumineux comme les vidéos ou les fichiers audio, présente des défis techniques. Les méthodes classiques utilisant des utilitaires de fichiers peuvent s'avérer inefficaces, entraînant des vitesses de transfert lentes et une absence de fonctionnalités avancées comme le téléversement fragmenté ou la reprise sur interruption. Alibaba Cloud Object Storage Service (OSS) offre une solution robuste pour ces scénarios, permettant des transferts rapides, sécurisés et résilients.

Utilisation de STS pour une authentification temporaire

Le service Security Token Service (STS) d'Alibaba Cloud permet de générer des credentials temporaires, réduisant ainsi les risques liés à l'exposition de clés d'accès permanentes. Ces tokens à durée de vie limitée offrent une gestion fine des permissions et facilitent le partage sécurisé de ressources.

Configuration préalable requise : Un compte Alibaba Cloud, un utilisateur RAM avec les permissions appropriées, et un rôle configuré. Les identifiants AccessKeyId et AccessKeySecret doivent être sécurisés.

Implémentation de la récupération et du cache du token STS

Pour éviter les limitations de taux et améliorer les performances, une mise en cache des tokens STS est mise en place avec Redis.

@Service
public class OssStsService {

    @Autowired
    private StringRedisTemplate stringRedisCache;

    private static final String STS_CACHE_KEY = "alibaba:oss:sts:credentials";
    private static final long STS_VALIDITY_SECONDS = 3500L;
    private static final long CACHE_REFRESH_BUFFER = 600L;

    private final String region = "oss-cn-beijing";
    private final String accessKeyId = "VOTRE_CLE_ID";
    private final String accessKeySecret = "VOTRE_CLE_SECRETE";
    private final String roleArn = "acs:ram::*:role/oss-upload-role";

    public Map<String, String> fetchStsCredentials() {
        String cachedCredsJson = stringRedisCache.opsForValue().get(STS_CACHE_KEY);
        if (StringUtils.hasText(cachedCredsJson)) {
            Map<String, String> cachedCreds = JSON.parseObject(cachedCredsJson,
                    new TypeReference<Map<String, String>>() {});
            long expiryTime = Long.parseLong(cachedCreds.get("expiresAt"));
            if (System.currentTimeMillis() + CACHE_REFRESH_BUFFER * 1000 < expiryTime) {
                return cachedCreds;
            }
        }
        return obtainNewStsToken();
    }

    private Map<String, String> obtainNewStsToken() {
        try {
            DefaultProfile profile = DefaultProfile.getProfile(region, accessKeyId, accessKeySecret);
            IAcsClient stsClient = new DefaultAcsClient(profile);

            AssumeRoleRequest tokenRequest = new AssumeRoleRequest();
            tokenRequest.setRoleArn(roleArn);
            tokenRequest.setRoleSessionName("sts-session-" + UUID.randomUUID().toString());
            tokenRequest.setDurationSeconds(STS_VALIDITY_SECONDS);

            String customPolicy = buildAccessPolicy();
            if (StringUtils.hasText(customPolicy)) {
                tokenRequest.setPolicy(customPolicy);
            }

            AssumeRoleResponse tokenResponse = stsClient.getAcsResponse(tokenRequest);
            AssumeRoleResponse.Credentials credentials = tokenResponse.getCredentials();

            Map<String, String> credentialMap = new HashMap<>();
            credentialMap.put("accessKeyId", credentials.getAccessKeyId());
            credentialMap.put("accessKeySecret", credentials.getAccessKeySecret());
            credentialMap.put("securityToken", credentials.getSecurityToken());
            credentialMap.put("expiresAt", String.valueOf(System.currentTimeMillis() + STS_VALIDITY_SECONDS * 1000));

            stringRedisCache.opsForValue().set(STS_CACHE_KEY,
                    JSON.toJSONString(credentialMap),
                    STS_VALIDITY_SECONDS, TimeUnit.SECONDS);
            return credentialMap;

        } catch (ClientException clientEx) {
            throw new RuntimeException("Échec de l'obtention du token STS", clientEx);
        }
    }

    private String buildAccessPolicy() {
        Map<String, Object> policyDocument = new HashMap<>();
        policyDocument.put("Version", "1");

        List<Map<String, Object>> permissionStatements = new ArrayList<>();
        Map<String, Object> statement = new HashMap<>();
        statement.put("Effect", "Allow");
        statement.put("Action", Arrays.asList("oss:PutObject", "oss:GetObject", "oss:ListObjects"));
        statement.put("Resource", Arrays.asList("acs:oss:*:*:monbucket/*"));
        permissionStatements.add(statement);

        policyDocument.put("Statement", permissionStatements);
        return JSON.toJSONString(policyDocument);
    }
}

Service de téléversement de fichier utilisant les credentials STS

Le contrôleur suivant expose un endpoint pour téléverser un fichier vers OSS en utilisant les credentials temporaires.

@RestController
@RequestMapping("/api/oss")
public class OssFileUploadController {

    private final String endpoint = "oss-cn-beijing.aliyuncs.com";
    private final String bucketName = "monbucket";

    @PostMapping("/upload")
    public String uploadFile(@RequestParam("fichier") MultipartFile fichier,
                             @RequestParam("accessKeyId") String temporaryAccessKeyId,
                             @RequestParam("accessKeySecret") String temporaryAccessKeySecret,
                             @RequestParam("securityToken") String stsToken) {

        ClientConfiguration ossConfig = new ClientConfiguration();
        ossConfig.setSocketTimeout(50000);
        ossConfig.setMaxErrorRetry(3);
        ossConfig.setConnectionTimeout(100000);

        String nomFichierOriginal = fichier.getOriginalFilename();
        String horodatage = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
        String nomObjetOss = horodatage + "_" + nomFichierOriginal;

        OSS ossClient = new OSSClientBuilder().build(endpoint,
                temporaryAccessKeyId, temporaryAccessKeySecret, stsToken, ossConfig);

        try {
            ObjectMetadata metadataFichier = new ObjectMetadata();
            String typeContenu = fichier.getContentType();
            if (typeContenu == null) {
                typeContenu = determinerTypeContenuParExtension(nomFichierOriginal);
            }
            metadataFichier.setContentType(typeContenu);
            metadataFichier.setObjectAcl(CannedAccessControlList.PublicRead);
            metadataFichier.setContentDisposition("attachment;filename=\"" + nomFichierOriginal + "\"");

            PutObjectRequest requeteTeleversement = new PutObjectRequest(bucketName,
                    nomObjetOss, fichier.getInputStream());
            requeteTeleversement.setMetadata(metadataFichier);

            ossClient.putObject(requeteTeleversement);

            String urlAcces = String.format("https://%s.%s/%s", bucketName, endpoint, nomObjetOss);
            return urlAcces;

        } catch (OSSException ossEx) {
            throw new RuntimeException("Erreur OSS : " + ossEx.getErrorMessage(), ossEx);
        } catch (ClientException clientEx) {
            throw new RuntimeException("Erreur client OSS", clientEx);
        } catch (IOException ioEx) {
            throw new RuntimeException("Erreur de lecture du fichier", ioEx);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    private String determinerTypeContenuParExtension(String nomFichier) {
        String extension = nomFichier.substring(nomFichier.lastIndexOf(".") + 1).toLowerCase();
        switch (extension) {
            case "jpg": case "jpeg": return "image/jpeg";
            case "png": return "image/png";
            case "pdf": return "application/pdf";
            case "mp4": return "video/mp4";
            default: return "application/octet-stream";
        }
    }
}

La configuration des permissions IAM sur Alibaba Cloud est cruciael pour le bon fonctionnement de cette intégration. Assurez-vous que le rôle RAM associé dispose des autorisations oss:PutObject, oss:GetObject, et oss:ListObjects sur le bucket cible.

Étiquettes: Alibaba Cloud oss Java SDK STS

Publié le 2 octobre à 22h23