feat: add download functionality for example operator and update Dock… (#188)

* feat: add download functionality for example operator and update Dockerfile

* feat: enhance download response by exposing content disposition header

* feat: update download function to accept filename parameter for example operator
This commit is contained in:
hhhhsc701
2025-12-22 15:39:32 +08:00
committed by GitHub
parent 8fc4455b57
commit 46f4a8c219
6 changed files with 64 additions and 1 deletions

View File

@@ -3,6 +3,7 @@ package com.datamate.operator.application;
import com.datamate.common.domain.model.ChunkUploadPreRequest; import com.datamate.common.domain.model.ChunkUploadPreRequest;
import com.datamate.common.domain.service.FileService; import com.datamate.common.domain.service.FileService;
import com.datamate.common.infrastructure.exception.BusinessException; import com.datamate.common.infrastructure.exception.BusinessException;
import com.datamate.common.infrastructure.exception.SystemErrorCode;
import com.datamate.operator.domain.contants.OperatorConstant; import com.datamate.operator.domain.contants.OperatorConstant;
import com.datamate.operator.infrastructure.converter.OperatorConverter; import com.datamate.operator.infrastructure.converter.OperatorConverter;
import com.datamate.operator.domain.model.OperatorView; import com.datamate.operator.domain.model.OperatorView;
@@ -21,10 +22,13 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils; import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.io.File; import java.io.File;
import java.net.MalformedURLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -165,6 +169,20 @@ public class OperatorService {
} }
} }
public Resource downloadExampleOperator(File file) {
try {
Resource resource = new UrlResource(file.toURI());
if (resource.exists()) {
return resource;
} else {
throw BusinessException.of(SystemErrorCode.RESOURCE_NOT_FOUND);
}
} catch (MalformedURLException ex) {
log.error("File not found: {}", file.getName(), ex);
throw BusinessException.of(SystemErrorCode.RESOURCE_NOT_FOUND);
}
}
private String convertObjectToListString(Object object) { private String convertObjectToListString(Object object) {
if (object == null) { if (object == null) {
return null; return null;

View File

@@ -1,5 +1,6 @@
package com.datamate.operator.interfaces.rest; package com.datamate.operator.interfaces.rest;
import com.datamate.common.infrastructure.common.IgnoreResponseWrap;
import com.datamate.common.interfaces.PagedResponse; import com.datamate.common.interfaces.PagedResponse;
import com.datamate.operator.application.OperatorService; import com.datamate.operator.application.OperatorService;
import com.datamate.operator.domain.contants.OperatorConstant; import com.datamate.operator.domain.contants.OperatorConstant;
@@ -8,9 +9,14 @@ import com.datamate.operator.interfaces.dto.OperatorsListPostRequest;
import com.datamate.operator.interfaces.dto.UploadOperatorRequest; import com.datamate.operator.interfaces.dto.UploadOperatorRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.io.File;
import java.util.List; import java.util.List;
@RestController @RestController
@@ -70,4 +76,23 @@ public class OperatorController {
public void operatorDelete(@PathVariable("id") String id) { public void operatorDelete(@PathVariable("id") String id) {
operatorService.deleteOperator(id); operatorService.deleteOperator(id);
} }
@IgnoreResponseWrap
@GetMapping(value = "/examples/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE + ";charset=UTF-8")
public ResponseEntity<Resource> downloadDatasetFileById() {
try {
File file = new File("/opt/backend/test_operator.tar");
Resource resource = operatorService.downloadExampleOperator(file);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION)
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getName() + "\"")
.body(resource);
} catch (IllegalArgumentException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
} }

View File

@@ -5,6 +5,7 @@ import {
EditOutlined, EditOutlined,
FilterOutlined, FilterOutlined,
PlusOutlined, PlusOutlined,
DownloadOutlined
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Boxes } from "lucide-react"; import { Boxes } from "lucide-react";
import { SearchControls } from "@/components/SearchControls"; import { SearchControls } from "@/components/SearchControls";
@@ -20,6 +21,7 @@ import { ListView } from "./components/List";
import useFetchData from "@/hooks/useFetchData"; import useFetchData from "@/hooks/useFetchData";
import { import {
deleteOperatorByIdUsingDelete, deleteOperatorByIdUsingDelete,
downloadExampleOperatorUsingGet,
queryCategoryTreeUsingGet, queryCategoryTreeUsingGet,
queryOperatorsUsingPost, queryOperatorsUsingPost,
} from "../operator.api"; } from "../operator.api";
@@ -58,6 +60,11 @@ export default function OperatorMarketPage() {
navigate(`/data/operator-market/create`); navigate(`/data/operator-market/create`);
}; };
const handleDownload = async () => {
await downloadExampleOperatorUsingGet("test_operator.tar");
message.success("文件下载成功");
};
const handleUpdateOperator = (operator: OperatorI) => { const handleUpdateOperator = (operator: OperatorI) => {
navigate(`/data/operator-market/create/${operator.id}`); navigate(`/data/operator-market/create/${operator.id}`);
}; };
@@ -119,7 +126,13 @@ export default function OperatorMarketPage() {
<div className="flex justify-between"> <div className="flex justify-between">
<h1 className="text-xl font-bold text-gray-900"></h1> <h1 className="text-xl font-bold text-gray-900"></h1>
<div className="flex gap-2"> <div className="flex gap-2">
<TagManagement /> {/*<TagManagement />*/}
<Button
icon={<DownloadOutlined />}
onClick={handleDownload}
>
</Button>
<Button <Button
type="primary" type="primary"
icon={<PlusOutlined />} icon={<PlusOutlined />}

View File

@@ -48,6 +48,11 @@ export function uploadOperatorChunkUsingPost(_, data: FormData, config?: any) {
...config, ...config,
}); });
} }
export function downloadExampleOperatorUsingGet(fileName: string) {
return download("/api/operators/examples/download", null, fileName);
}
// 发布算子 // 发布算子
export function publishOperatorUsingPost(operatorId: string | number) { export function publishOperatorUsingPost(operatorId: string | number) {
return post(`/api/operators/${operatorId}/publish`); return post(`/api/operators/${operatorId}/publish`);

Binary file not shown.

View File

@@ -31,6 +31,8 @@ COPY --from=datax-builder /DataX/target/datax/datax /opt/datax
COPY scripts/images/backend/start.sh /opt/backend/start.sh COPY scripts/images/backend/start.sh /opt/backend/start.sh
COPY runtime/ops/examples/test_operator/test_operator.tar /opt/backend/test_operator.tar
RUN dos2unix /opt/backend/start.sh \ RUN dos2unix /opt/backend/start.sh \
&& chmod +x /opt/backend/start.sh \ && chmod +x /opt/backend/start.sh \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime