Browse Source

Edit file multipart

tsurumure 4 tháng trước cách đây
mục cha
commit
b80af77193

+ 5 - 2
db/sys_file.sql

@@ -10,7 +10,6 @@ CREATE TABLE `sys_file` (
     `id` BIGINT AUTO_INCREMENT COMMENT 'ID',
     `category_id` BIGINT COMMENT '分类ID',
     `request_id` VARCHAR(100) COMMENT '请求响应ID',
-    `upload_id` VARCHAR(100) COMMENT '分块上传ID',
     `user_id` BIGINT COMMENT '上传用户ID',
     `name` VARCHAR(255) COMMENT '文件名称',
     `content_type` VARCHAR(255) COMMENT '文件类型',
@@ -19,7 +18,11 @@ CREATE TABLE `sys_file` (
     `object_key` VARCHAR(500) NOT NULL COMMENT 'ObjectKey',
     `size` INT COMMENT '文件大小',
     `md5` VARCHAR(255) COMMENT '文件MD5',
+    `upload_id` VARCHAR(100) COMMENT '分块上传ID',
+    `upload_chunk_count` INT COMMENT '分块文件数量',
+    `upload_chunk_index` INT COMMENT '分块文件索引',
     `target` INT COMMENT '上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)',
     `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
+    `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    UNIQUE KEY (`upload_id`)
 ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='系统上传文件表';

+ 2 - 1
src/main/java/com/backendsys/modules/cms/siteinfo/service/impl/SiteInfoServiceImpl.java

@@ -35,9 +35,10 @@ public class SiteInfoServiceImpl implements SiteInfoService {
 
         String lang = DEFAULT_LANGUAGE;
 
-        // [Header] 从头部信息来的 Lang
         HttpServletRequest request = httpRequestUtil.getRequest();
         String headerLang = request.getHeader("Lang");
+
+        // [Header] 从头部信息来的 Lang
         if (StrUtil.isNotEmpty(headerLang)) lang = headerLang;
         // [Param] 从参数来的 Lang
         if (StrUtil.isNotEmpty(paramLang)) lang = paramLang;

+ 26 - 64
src/main/java/com/backendsys/modules/sdk/tencentcloud/cos/service/impl/TencentCosServiceImpl.java

@@ -277,13 +277,6 @@ public class TencentCosServiceImpl implements TencentCosService {
         InputStream inputStream = null;
         try {
 
-            // "The parameter partNumber is not valid
-            System.out.println("partNumber = " + partNumber);
-            long partSize = 524288;
-
-
-            // 加 partNumber 和 partSize 入参吧
-
             UploadPartRequest uploadPartRequest = new UploadPartRequest();
             uploadPartRequest.setBucketName(BUCKET_NAME);
             uploadPartRequest.setKey(object_key);
@@ -291,44 +284,10 @@ public class TencentCosServiceImpl implements TencentCosService {
 
             inputStream = multipartFile.getInputStream();
             uploadPartRequest.setInputStream(inputStream);
-//            uploadPartRequest.setPartSize(multipartFile.getSize());     // 设置分块的长度
-            uploadPartRequest.setPartSize(partSize);     // 设置分块的长度
-//            uploadPartRequest.setPartNumber(partNumber);                // 设置要上传的分块编号,从 1 开始
-            uploadPartRequest.setPartNumber(3);                // 设置要上传的分块编号,从 1 开始
+            uploadPartRequest.setPartSize(multipartFile.getSize());     // 设置分块的长度
+            uploadPartRequest.setPartNumber(partNumber);                // 设置要上传的分块编号,从 1 开始
             return cosClient.uploadPart(uploadPartRequest);
 
-
-//            // 每个分块上传之后都会得到一个返回值 etag,保存起来用于最后合并分块时使用
-//            List<PartETag> partETags = new LinkedList<>();
-//            // 上传数据, 这里上传 10 个 1M 的分块数据
-//            for (int i = 1; i <= 10; i++) {
-//                System.out.println("i = " + i);
-//                // 这里创建一个 ByteArrayInputStream 来作为示例,实际中这里应该是您要上传的 InputStream 类型的流
-//                int inputStreamLength = 1024 * 1024;
-//                byte data[] = new byte[inputStreamLength];
-//                InputStream inputStream = new ByteArrayInputStream(data);
-//                UploadPartRequest uploadPartRequest = new UploadPartRequest();
-//                uploadPartRequest.setBucketName(BUCKET_NAME);
-//                uploadPartRequest.setKey(object_key);
-//                uploadPartRequest.setUploadId(upload_id);
-//                uploadPartRequest.setInputStream(inputStream);
-//                // 设置分块的长度
-//                uploadPartRequest.setPartSize(data.length);
-//                // 设置要上传的分块编号,从 1 开始
-//                uploadPartRequest.setPartNumber(i);
-//                try {
-//                    UploadPartResult uploadPartResult = cosClient.uploadPart(uploadPartRequest);
-//                    PartETag partETag = uploadPartResult.getPartETag();
-//                    partETags.add(partETag);
-//                } catch (CosServiceException e) {
-//                    throw e;
-//                } catch (CosClientException e) {
-//                    throw e;
-//                }
-//            }
-//            return null;
-
-
         } catch (IOException | CosClientException e) {
             throw new CustException(e.getMessage());
         } finally {
@@ -343,6 +302,30 @@ public class TencentCosServiceImpl implements TencentCosService {
         }
     }
 
+    // [腾讯云COS] 完成分块上传
+    @Override
+    public CompleteMultipartUploadResult completeMultipartUpload(String upload_id, String object_key, List<PartETag> partETags) {
+
+        if (StrUtil.isEmpty(upload_id)) throw new CustException("upload_id 不能为空");
+        if (StrUtil.isEmpty(object_key)) throw new CustException("object_key 不能为空");
+
+
+
+
+        COSClient cosClient = getClient();
+        try {
+
+            // 分片上传结束后,调用 complete 完成分片上传
+            CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(BUCKET_NAME, object_key, upload_id, partETags);
+            return cosClient.completeMultipartUpload(completeMultipartUploadRequest);
+
+        } catch (CosClientException e) {
+            throw new CustException(e.getMessage());
+        } finally {
+            if (cosClient != null) cosClient.shutdown();
+        }
+    }
+
 
     // [腾讯云COS] 查询已上传的分块
     @Override
@@ -365,8 +348,6 @@ public class TencentCosServiceImpl implements TencentCosService {
                     partETags.add(new PartETag(partSummary.getPartNumber(), partSummary.getETag()));
                     System.out.println("list multipart upload parts, partNum:" + partSummary.getPartNumber() + ", etag:" + partSummary.getETag());
                 }
-                System.out.println("nextPartNumberMarker: " + partListing.getNextPartNumberMarker());
-
                 listPartsRequest.setPartNumberMarker(partListing.getNextPartNumberMarker());
                 return partListing;
             } while (partListing.isTruncated());
@@ -377,24 +358,5 @@ public class TencentCosServiceImpl implements TencentCosService {
     }
 
 
-    // [腾讯云COS] 完成分块上传
-    @Override
-    public CompleteMultipartUploadResult completeMultipartUpload(String upload_id, String object_key, List<PartETag> partETags) {
-
-        if (StrUtil.isEmpty(upload_id)) throw new CustException("upload_id 不能为空");
-        if (StrUtil.isEmpty(object_key)) throw new CustException("object_key 不能为空");
 
-        COSClient cosClient = getClient();
-        try {
-
-            // 分片上传结束后,调用 complete 完成分片上传
-            CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(BUCKET_NAME, object_key, upload_id, partETags);
-            return cosClient.completeMultipartUpload(completeMultipartUploadRequest);
-
-        } catch (CosClientException e) {
-            throw new CustException(e.getMessage());
-        } finally {
-            if (cosClient != null) cosClient.shutdown();
-        }
-    }
 }

+ 14 - 6
src/main/java/com/backendsys/modules/upload/controller/SysFileMultipartController.java

@@ -33,17 +33,25 @@ public class SysFileMultipartController {
     @SysLog("初始化分块上传")
     @Operation(summary = "初始化分块上传")
     @PreAuthorize("@sr.hasPermission(1.1)")
-    @PostMapping("/api/upload/initiateMultipartUpload")
-    public Result initiateMultipartUpload(@RequestParam("file") MultipartFile multipartFile, Long category_id) {
-        return Result.success().put("data", sysFileMultipartService.initiateMultipartUpload(multipartFile, category_id));
+    @PostMapping("/api/upload/multipartUploadInit")
+    public Result multipartUploadInit(@RequestParam("file") MultipartFile multipartFile, Long category_id, Integer upload_chunk_count) {
+        return Result.success().put("data", sysFileMultipartService.multipartUploadInit(multipartFile, category_id, upload_chunk_count));
     }
 
     @SysLog("上传分块")
     @Operation(summary = "上传分块")
     @PreAuthorize("@sr.hasPermission(1.1)")
-    @PostMapping("/api/upload/uploadMultipart")
-    public Result uploadMultipart(@RequestParam("file") MultipartFile multipartFile, String upload_id) {
-        return Result.success().put("data", sysFileMultipartService.uploadMultipart(multipartFile, upload_id));
+    @PostMapping("/api/upload/multipartUpload")
+    public Result multipartUpload(@RequestParam("file") MultipartFile multipartFile, String upload_id, Integer upload_chunk_index) {
+        return Result.success().put("data", sysFileMultipartService.multipartUpload(multipartFile, upload_id, upload_chunk_index));
+    }
+
+    @SysLog("合并分块")
+    @Operation(summary = "合并分块")
+    @PreAuthorize("@sr.hasPermission(1.1)")
+    @PostMapping("/api/upload/multipartUploadComplete")
+    public Result multipartUploadComplete(String upload_id) {
+        return Result.success().put("data", sysFileMultipartService.multipartUploadComplete(upload_id));
     }
 
     @Operation(summary = "查询分块上传情况")

+ 5 - 1
src/main/java/com/backendsys/modules/upload/entity/SysFile.java

@@ -26,7 +26,11 @@ public class SysFile {
     @TableField(exist = false)
     private String category_name;
     private String request_id;
+
     private String upload_id;
+    private Integer upload_chunk_count;
+    private Integer upload_chunk_index;
+
     private Long user_id;
 
     @NotEmpty(message = "文件名不能为空", groups = { Update.class })
@@ -35,9 +39,9 @@ public class SysFile {
     private String content_type;
     private String url;
     private String url_thumb;
+
     @NotEmpty(message = "object_key 不能为空", groups = { Delete.class })
     private String object_key;
-
     @TableField(exist = false)
     @NotNull(message = "object_keys 不能为空", groups = { DeleteBatch.class })
     private String object_keys;

+ 6 - 7
src/main/java/com/backendsys/modules/upload/service/SysFileMultipartService.java

@@ -7,15 +7,14 @@ import java.util.Map;
 
 public interface SysFileMultipartService {
 
-    // 初始化分块上传
-    Map<String, Object> initiateMultipartUpload(MultipartFile multipartFile, Long category_id);
-
-    // 上传分块
-    Map<String, Object> uploadMultipart(MultipartFile multipartFile, String upload_id);
+    // 1.初始化分块上传
+    Map<String, Object> multipartUploadInit(MultipartFile multipartFile, Long category_id, Integer upload_chunk_count);
+    // 2.上传分块
+    Map<String, Object> multipartUpload(MultipartFile multipartFile, String upload_id, Integer upload_chunk_index);
+    // 3.完成分块上传
+    Map<String, Object> multipartUploadComplete(String upload_id);
 
     // 查询分块上传情况
     PartListing listParts(String upload_id, String object_key);
 
-    // 完成分块上传
-    Map<String, Object> completeMultipartUpload(String upload_id);
 }

+ 75 - 30
src/main/java/com/backendsys/modules/upload/service/impl/SysFileMultipartServiceImpl.java

@@ -19,10 +19,12 @@ import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
 
 @Service
 public class SysFileMultipartServiceImpl implements SysFileMultipartService {
@@ -41,9 +43,12 @@ public class SysFileMultipartServiceImpl implements SysFileMultipartService {
         查一下 upload_id 多久会过期
      */
 
-    // 初始化分块上传 (获得 upload_id, object_key)
+    // 1.初始化分块上传 (获得 upload_id, object_key)
     @Override
-    public Map<String, Object> initiateMultipartUpload(MultipartFile multipartFile, Long category_id) {
+    public Map<String, Object> multipartUploadInit(MultipartFile multipartFile, Long category_id, Integer upload_chunk_count) {
+
+        if (multipartFile.isEmpty()) throw new CustException("file 上传文件不能为空");
+        if (upload_chunk_count == null) throw new CustException("upload_chunk_count 分块数量不能为空");
 
         // 获得公共配置
         List<SysCommon> sysCommonList = sysCommonService.getCommonByCategory("UPLOAD");
@@ -54,12 +59,13 @@ public class SysFileMultipartServiceImpl implements SysFileMultipartService {
 
         System.out.println("[系统配置] 上传目标(-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云): " + UPLOAD_TARGET);
 
-
+        String target_label = "";
         String upload_id = null;
         String object_key = null;
 
         // target: 上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)
         if (UPLOAD_TARGET.get() == 1) {
+            target_label = "腾讯云初始化分块";
             InitiateMultipartUploadResult uploadResult = tencentCosService.initiateMultipartUpload(multipartFile);
             upload_id = uploadResult.getUploadId();
             object_key = uploadResult.getKey();
@@ -75,6 +81,8 @@ public class SysFileMultipartServiceImpl implements SysFileMultipartService {
         sysFileEntity.setCategory_id(category_id);
         sysFileEntity.setUser_id(httpRequestUtil.getUserId());
         sysFileEntity.setUpload_id(upload_id);
+        sysFileEntity.setUpload_chunk_count(upload_chunk_count);
+        sysFileEntity.setUpload_chunk_index(0);
         sysFileEntity.setName(FileNameUtil.getName(object_key));
         sysFileEntity.setObject_key(object_key);
         sysFileEntity.setTarget(UPLOAD_TARGET.get());
@@ -83,64 +91,101 @@ public class SysFileMultipartServiceImpl implements SysFileMultipartService {
         sysFileDao.insert(sysFileEntity);
 
         Map<String, Object> resp = new LinkedHashMap<>();
+        resp.put("target", UPLOAD_TARGET.get());
+        resp.put("target_label", target_label);
+        resp.put("upload_chunk_count", upload_chunk_count);
         resp.put("upload_id", upload_id);
         resp.put("object_key", object_key);
         return resp;
     }
 
-    // 上传分块
+    // 2.上传分块
     @Override
-    public Map<String, Object> uploadMultipart(MultipartFile multipartFile, String upload_id) {
+    public Map<String, Object> multipartUpload(MultipartFile multipartFile, String upload_id, Integer upload_chunk_index) {
 
+        if (multipartFile.isEmpty()) throw new CustException("file 上传文件不能为空");
         if (StrUtil.isEmpty(upload_id)) throw new CustException("upload_id 不能为空");
+        if (upload_chunk_index == null) throw new CustException("upload_chunk_index 不能为空");
 
+        // [Get] 获得初始化分块信息
         SysFile sysFileEntity = sysFileDao.selectOne(new LambdaQueryWrapper<SysFile>().eq(SysFile::getUpload_id, upload_id));
-        if (sysFileEntity == null) throw new CustException("upload_id 不存在");
-
+        if (sysFileEntity == null) throw new CustException("分块记录不存在");
         String object_key = sysFileEntity.getObject_key();
+        Integer upload_chunk_size = sysFileEntity.getUpload_chunk_count();
+        if (upload_chunk_index > upload_chunk_size) throw new CustException("分块索引(index)不能大于分块数量(count)");
+
+        // 获得公共配置
+        List<SysCommon> sysCommonList = sysCommonService.getCommonByCategory("UPLOAD");
+        AtomicReference<Integer> UPLOAD_TARGET = new AtomicReference<>();
+        sysCommonList.stream().forEach(sysCommon -> {
+            if (sysCommon.getTag().equals("UPLOAD_TARGET")) UPLOAD_TARGET.set(Convert.toInt(sysCommon.getValue()));
+        });
+
+        System.out.println("[系统配置] 上传目标(-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云): " + UPLOAD_TARGET);
+
+        PartETag part_etag = null;
+
+        // target: 上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)
+        if (UPLOAD_TARGET.get() == 1) {
+            UploadPartResult partResult = tencentCosService.uploadPart(multipartFile, upload_id, object_key, upload_chunk_index);
+            System.out.println("partResult:");
+            System.out.println(partResult);
+            part_etag = partResult.getPartETag();
+        }
 
-        // 查询分块上传情况 (获得下次要上传的位置)
-        PartListing partListing = listParts(upload_id, object_key);
-        Integer partNumberMarker = partListing.getPartNumberMarker() + 1;
+//        // 查询分块上传情况 (获得下次要上传的位置)
+//        PartListing partListing = listParts(upload_id, object_key);
+//        Integer partNumberMarker = partListing.getPartNumberMarker() + 1;
 
-        UploadPartResult partResult = tencentCosService.uploadPart(multipartFile, upload_id, object_key, partNumberMarker);
-        System.out.println("partResult:");
-        System.out.println(partResult);
+
+        // [Update] 更新分块文件信息
+        sysFileEntity.setUpload_chunk_index(upload_chunk_index);
+        sysFileDao.updateById(sysFileEntity);
 
         Map<String, Object> resp = new LinkedHashMap<>();
+        resp.put("upload_chunk_index", upload_chunk_index);
         resp.put("upload_id", upload_id);
         resp.put("object_key", object_key);
-        resp.put("part_number", partNumberMarker);
-        resp.put("part_etag", partResult.getPartETag());
+        resp.put("part_etag", part_etag);
         return resp;
     }
 
-    // 查询分块上传情况
-    @Override
-    public PartListing listParts(String upload_id, String object_key) {
-        return tencentCosService.listParts(upload_id, object_key);
-    }
-
     // 完成分块上传
     @Override
-    public Map<String, Object> completeMultipartUpload(String upload_id) {
+    public Map<String, Object> multipartUploadComplete(String upload_id) {
 
         if (StrUtil.isEmpty(upload_id)) throw new CustException("upload_id 不能为空");
 
+        // [Get] 查询文件分块记录
         SysFile sysFileEntity = sysFileDao.selectOne(new LambdaQueryWrapper<SysFile>().eq(SysFile::getUpload_id, upload_id));
         if (sysFileEntity == null) throw new CustException("upload_id 不存在");
 
-        String object_key = sysFileEntity.getObject_key();
+        // 判断分块索引和分块数量是否一致
+        if (sysFileEntity.getUpload_chunk_index() != sysFileEntity.getUpload_chunk_count()) {
+            throw new CustException("分块索引和分块数量不一致");
+        }
 
-//        // 查询分块上传情况 (获得下次要上传的位置)
-//        PartListing partListing = listParts(upload_id, object_key);
-//        List<PartSummary> partSummaryList = partListing.getParts();
-//
-//        List<PartETag> partETags = new ArrayList<>();
-//
-//        tencentCosService.completeMultipartUpload(upload_id, object_key, partETags);
+        // 查询分块集合 (etag)
+        PartListing partListing = listParts(upload_id, sysFileEntity.getObject_key());
+        List<PartSummary> partSummaryList = partListing.getParts();
+        List<PartETag> etags = partSummaryList.stream()
+            .map(partSummary -> new PartETag(partSummary.getPartNumber(), partSummary.getETag()))
+            .collect(Collectors.toList());
+
+        // 如果分块数量对不上,最好还是重新上传
+        if (sysFileEntity.getUpload_chunk_count() != etags.size()) {
+            throw new CustException("分块索引异常,请重新上传");
+        }
 
+        // 合并分块
+        tencentCosService.completeMultipartUpload(upload_id, sysFileEntity.getObject_key(), etags);
         return Map.of("upload_id", upload_id);
     }
 
+    // 查询分块上传情况
+    @Override
+    public PartListing listParts(String upload_id, String object_key) {
+        return tencentCosService.listParts(upload_id, object_key);
+    }
+
 }

+ 14 - 12
src/main/java/com/backendsys/modules/upload/service/impl/SysFileServiceImpl.java

@@ -60,18 +60,20 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
     //   2:阿里云:
     //   3:抖音云: https://www.volcengine.com/docs/6349/153626
     private SysFile setThumbUrl(SysFile sysFile, Integer width, Integer height, String backgroundColor) {
-        // 本地上传
-        if (sysFile.getTarget() == -1) {
-            sysFile.setUrl_thumb(sysFile.getUrl() + "?w=" + width + "&h=" + height);
-        }
-        // 腾讯云 (color值通过base64加密, #f8f8f8)
-        if (sysFile.getTarget() == 1) {
-            System.out.println("base64 encode: " + Base64.encode(backgroundColor));
-            sysFile.setUrl_thumb(sysFile.getUrl() + "?imageMogr2/thumbnail/" + width + "x" + height + "/pad/1/color/" + Base64.encode(backgroundColor));
-        }
-        // 抖音云
-        if (sysFile.getTarget() == 3) {
-            sysFile.setUrl_thumb(sysFile.getUrl() + "?x-tos-process=image/resize,w_" + width + ",h_" + height + ",m_pad,color_" + backgroundColor);
+        if (StrUtil.isEmpty(sysFile.getUpload_id())) {
+            // 本地上传
+            if (sysFile.getTarget() == -1) {
+                sysFile.setUrl_thumb(sysFile.getUrl() + "?w=" + width + "&h=" + height);
+            }
+            // 腾讯云 (color值通过base64加密, #f8f8f8)
+            if (sysFile.getTarget() == 1) {
+                System.out.println("base64 encode: " + Base64.encode(backgroundColor));
+                sysFile.setUrl_thumb(sysFile.getUrl() + "?imageMogr2/thumbnail/" + width + "x" + height + "/pad/1/color/" + Base64.encode(backgroundColor));
+            }
+            // 抖音云
+            if (sysFile.getTarget() == 3) {
+                sysFile.setUrl_thumb(sysFile.getUrl() + "?x-tos-process=image/resize,w_" + width + ",h_" + height + ",m_pad,color_" + backgroundColor);
+            }
         }
         return sysFile;
     }

+ 7 - 0
src/main/resources/mapper/upload/SysFileDao.xml

@@ -8,6 +8,8 @@
         COALESCE(fc.category_name, '') category_name,
         COALESCE(f.request_id, '') request_id,
         COALESCE(f.upload_id, '') upload_id,
+        COALESCE(f.upload_chunk_count, '') upload_chunk_count,
+        COALESCE(f.upload_chunk_index, '') upload_chunk_index,
         f.user_id,
         f.name,
         f.content_type,
@@ -28,6 +30,8 @@
         <result property="category_name" column="category_name" />
         <result property="request_id" column="request_id" />
         <result property="upload_id" column="upload_id" />
+        <result property="upload_chunk_count" column="upload_chunk_count" javaType="java.lang.Integer" />
+        <result property="upload_chunk_index" column="upload_chunk_index" javaType="java.lang.Integer" />
         <result property="user_id" column="user_id" javaType="java.lang.Long" />
         <result property="name" column="name" />
         <result property="content_type" column="content_type" />
@@ -55,6 +59,9 @@
             <if test="user_id != null and user_id != ''">
                 AND f.user_id = #{user_id}
             </if>
+            <if test="upload_id != null and upload_id != ''">
+                AND f.upload_id = #{upload_id}
+            </if>
             <if test="object_key != null and object_key != ''">
                 AND f.object_key = #{object_key}
             </if>