如何无需签名为公共文件URL设置自定义下载文件名?
解决方案:无需签名URL为公共S3/GCS文件设置自定义下载文件名
针对你的需求,分两种场景给出可行方案:
一、你拥有S3/GCS桶的控制权(可修改对象元数据)
这是最直接的方案,通过给公共对象配置Content-Disposition元数据,让浏览器自动使用自定义文件名下载。
AWS S3 实现(Java SDK 2.x)
S3不支持直接修改对象元数据,需通过复制对象到自身的方式更新元数据:
import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.CopyObjectRequest; import software.amazon.awssdk.services.s3.model.MetadataDirective; public class S3UpdateMetadata { public static void setCustomDownloadFilename(String bucketName, String objectKey, String customFilename) { try (S3Client s3Client = S3Client.create()) { CopyObjectRequest copyRequest = CopyObjectRequest.builder() .sourceBucket(bucketName) .sourceKey(objectKey) .destinationBucket(bucketName) .destinationKey(objectKey) .metadataDirective(MetadataDirective.REPLACE) .contentDisposition("attachment; filename=\"" + customFilename + "\"") .build(); s3Client.copyObject(copyRequest); } } }
配置完成后,直接访问该对象的公共URL,浏览器下载时会自动使用你设置的customFilename。
Google Cloud Storage 实现(Java SDK)
GCS支持直接更新对象元数据,无需复制:
import com.google.cloud.storage.Blob; import com.google.cloud.storage.BlobId; import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageOptions; public class GCSUpdateMetadata { public static void setCustomDownloadFilename(String bucketName, String objectName, String customFilename) { Storage storage = StorageOptions.getDefaultInstance().getService(); BlobId blobId = BlobId.of(bucketName, objectName); Blob blob = storage.get(blobId); blob.toBuilder() .setContentDisposition("attachment; filename=\"" + customFilename + "\"") .build() .update(); } }
更新后,公共URL访问时浏览器会自动应用自定义文件名。
二、无法修改对象元数据(第三方公共文件)
如果是他人的公共S3/GCS文件,你无法修改其元数据,可通过以下两种方式实现:
方案1:Java后端中转下载
后端请求公共文件,在返回给前端的响应中设置Content-Disposition头:
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.client.RestTemplate; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; @GetMapping("/download-custom") public ResponseEntity<byte[]> downloadWithCustomName(@RequestParam String publicUrl, @RequestParam String customFilename) throws Exception { RestTemplate restTemplate = new RestTemplate(); InputStream inputStream = restTemplate.getForObject(publicUrl, InputStream.class); // 临时保存文件流(也可直接写入响应输出流) Path tempFile = Files.createTempFile("temp", null); Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING); byte[] fileBytes = Files.readAllBytes(tempFile); HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + customFilename + "\""); return new ResponseEntity<>(fileBytes, headers, HttpStatus.OK); }
前端访问这个后端接口,即可下载带有自定义文件名的文件。
方案2:前端JS处理(注意CORS限制)
通过前端Fetch API获取文件Blob,再触发下载:
async function downloadWithCustomName(publicUrl, customFilename) { try { const response = await fetch(publicUrl); const blob = await response.blob(); const blobUrl = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = blobUrl; link.download = customFilename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(blobUrl); } catch (error) { console.error('下载失败:', error); } }
注意:此方案要求公共文件所在的S3/GCS桶已配置CORS,允许你的前端域名发起跨域请求,否则会出现权限错误。
内容的提问来源于stack exchange,提问作者Sudhanshu Bansal
相关产品推荐
相关产品推荐

