第四章:minio的presigned URLs上传文件

章节快捷访问:

第一章:minio介绍与安装

https://blog.csdn.net/hzw2312/article/details/106077729

第二章:minio单机版,使用客户端备份文件

https://blog.csdn.net/hzw2312/article/details/106078150

第三章:minio的javaAPI

https://blog.csdn.net/hzw2312/article/details/106078390

第四章:minio的presigned URLs上传文件

https://blog.csdn.net/hzw2312/article/details/106078604

--------------------------------------------------

 

当我们通过java的API上传文件的时候就会发现,我们把java的API封装了一下,提供了一个接口给其他应用调用,那么整个的上传流程就变成了“应用客户端”-->“JavaAPI端”-->“minio服务端”。中间通过JavaAPI转了一次。比如我们的“应用客户端”是web浏览器的时候,能不能直接从浏览器上传到“minio服务端”呢?答案是可以的,minio有提供JSSDK,我们可以通过JSAPI直接上传到“minio服务端”。

JSSDK

参考官网的文档:https://docs.min.io/cn/javascript-client-quickstart-guide.html

var Minio = require('minio')

// Instantiate the minio client with the endpoint
// and access keys as shown below.
var minioClient = new Minio.Client({
    endPoint: 'play.min.io',
    port: 9000,
    useSSL: true,
    accessKey: 'Q3AM3UQ867SPQQA43P2F',
    secretKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG'
});

// File that needs to be uploaded.
var file = '/tmp/photos-europe.tar'

// Make a bucket called europetrip.
minioClient.makeBucket('europetrip', 'us-east-1', function(err) {
    if (err) return console.log(err)

    console.log('Bucket created successfully in "us-east-1".')

    var metaData = {
        'Content-Type': 'application/octet-stream',
        'X-Amz-Meta-Testing': 1234,
        'example': 5678
    }
    // Using fPutObject API upload your file to the bucket europetrip.
    minioClient.fPutObject('europetrip', 'photos-europe.tar', file, metaData, function(err, etag) {
      if (err) return console.log(err)
      console.log('File uploaded successfully.')
    });
});

根据官网的文档,我们可以看到,从js上传文件需要将我们的accessKey跟secretKey暴露出去。这样的话,别人拿到我们的key岂不是可以为所欲为了!

这样是很危险的,所以我们不能这样做。除非你是用这段脚本来写node.js服务,把他当作服务端。但是这样的话,岂不是又是通过“应用客户端”-->“api端”-->“minio服务端”这样的路径来进行上传了。

presigned URLs

minio并没有像商用的七牛云存储那样,可以通过临时的token来上传文件。但是,这个但是很重要啊,他提供了使用pre-signed URLs通过浏览器上传文件。那么这个pre-signed URLs是啥子?pre-signed URLs简单来说就是预处理签名的一个URL。就是url中包含了签名信息,但是是经过加密的。而且还有时效性的。这样一来就跟七牛云的临时token有异曲同工之处了。既保证了用最短的途径能上传文件,又保证了我们的上传签名信息不会被恶意利用。

官网的文档:https://docs.min.io/cn/upload-files-from-browser-using-pre-signed-urls.html

这里官网给出的例子是node.js的,我们使用Java该怎么用呢!

首先在我们之前的MinioTemplate类中,加上支持方法:

/**
 * 获得上传的URL
 * @param bucketName
 * @param objectName
 * @param expires
 * @return
 * @throws IOException
 * @throws InvalidKeyException
 * @throws NoSuchAlgorithmException
 * @throws InsufficientDataException
 * @throws InvalidExpiresRangeException
 * @throws InternalException
 * @throws NoResponseException
 * @throws InvalidBucketNameException
 * @throws XmlPullParserException
 * @throws ErrorResponseException
 */
public String presignedPutObject(String bucketName, String objectName, Integer expires) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidExpiresRangeException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException {
    if(expires == null){
        // 7天
        return client.presignedPutObject(bucketName,objectName);
    }
    return client.presignedPutObject(bucketName,objectName,expires);
}

然后在我们之前的控制器中加上方法:

/**
 * 获得上传的URL
 * @param bucketName    文件桶名称
 * @param objectName    文件对象名称:(pdf/2019/0512/test.pdf)
 */
@ApiOperation(value = "获得上传的URL", response = ActionResult.class)
@GetMapping("/uploadUrl")
public ActionResult<Object> uploadUrl(
        @ApiParam(name = "bucketName",value = "文件桶名称") String bucketName,
        @ApiParam(name = "objectName",value = "文件对象名称:(pdf/2019/0512/test.pdf)") String objectName,
        @ApiParam(name = "expires", value ="链接有效时间(单位秒)") Integer expires)
        throws IOException, XmlPullParserException, NoSuchAlgorithmException, InvalidKeyException, InvalidPortException, ErrorResponseException, NoResponseException, InvalidBucketNameException, InsufficientDataException,  InternalException, InvalidExpiresRangeException {
    String url = template.presignedPutObject(bucketName,objectName,expires);
    return success(url,"获取成功");
}

这样js在上传之前先调用我们的uploadUrl方法得到我们的上传URL

在之前我们配置java的api链接的是51.78服务端,那么我们先来看看里面有什么文件。

可以看到,只有一张图片,下面我们调用java获取上传URL的方法:

可以看到我们调用方法,得到了一个带有签名信息的URL,下面我将使用这个url来进行文件的上传:

需要注意一下,这里是要发送一个PUT请求,还有上传的是binary二进制文件流。

这里上传成功后,并没有返回任何信息...我们打开51.78服务来看看

文件已经成功上传了,在看看我们的51.80备份库。

我们看到,也备份成功了。这一串路径是因为51.78文件服务的存储地址就在这个路径下面,所以备份的时候也将这些路径带来了。

这里需要注意的是 ,我使用的是Postman测试工具来测试的,如果使用前端测试,需要注意跨域的问题,官网推荐使用的上传js工具包是Fetch。我们看看官网的例子:

<input type="file" id="selector" multiple>
<button οnclick="upload()">Upload</button>

<div id="status">No uploads</div>

<script type="text/javascript">
    // `upload` iterates through all files selected and invokes a helper function called `retrieveNewURL`.
    function upload() {
        // Get selected files from the input element.
        var files = document.querySelector("#selector").files;
        for (var i = 0; i < files.length; i++) {
            var file = files[i];
            // 从服务器获取一个URL
            retrieveNewURL(file, (file, url) => {
                // 上传文件到服务器
                uploadFile(file, url);
            });
        }
    }

    // 发请求到Node.js server获取上传URL。
    // `retrieveNewURL` accepts the name of the current file and invokes the `/presignedUrl` endpoint to
    // generate a pre-signed URL for use in uploading that file: 
    function retrieveNewURL(file, cb) {
        fetch(`/presignedUrl?name=${file.name}`).then((response) => {
            response.text().then((url) => {
                cb(file, url);
            });
        }).catch((e) => {
            console.error(e);
        });
    }

    // 使用Fetch API来上传文件到S3。
    // ``uploadFile` accepts the current filename and the pre-signed URL. It then uses `Fetch API`
    // to upload this file to S3 at `play.min.io:9000` using the URL:
    function uploadFile(file, url) {
        if (document.querySelector('#status').innerText === 'No uploads') {
            document.querySelector('#status').innerHTML = '';
        }
        fetch(url, {
            method: 'PUT',
            body: file
        }).then(() => {
            // If multiple files are uploaded, append upload status on the next line.
            document.querySelector('#status').innerHTML += `<br>Uploaded ${file.name}.`;
        }).catch((e) => {
            console.error(e);
        });
    }
</script>

好了,单机版本基本就结束了,如果后续还有啥要补充的,我会持续更新的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章