Can download disk images
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		@@ -3,9 +3,10 @@ use crate::constants;
 | 
				
			|||||||
use crate::controllers::HttpResult;
 | 
					use crate::controllers::HttpResult;
 | 
				
			||||||
use crate::utils::file_disks_utils::DiskFileInfo;
 | 
					use crate::utils::file_disks_utils::DiskFileInfo;
 | 
				
			||||||
use crate::utils::files_utils;
 | 
					use crate::utils::files_utils;
 | 
				
			||||||
 | 
					use actix_files::NamedFile;
 | 
				
			||||||
use actix_multipart::form::MultipartForm;
 | 
					use actix_multipart::form::MultipartForm;
 | 
				
			||||||
use actix_multipart::form::tempfile::TempFile;
 | 
					use actix_multipart::form::tempfile::TempFile;
 | 
				
			||||||
use actix_web::{HttpResponse, web};
 | 
					use actix_web::{HttpRequest, HttpResponse, web};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, MultipartForm)]
 | 
					#[derive(Debug, MultipartForm)]
 | 
				
			||||||
pub struct UploadDiskImageForm {
 | 
					pub struct UploadDiskImageForm {
 | 
				
			||||||
@@ -73,6 +74,23 @@ pub struct DiskFilePath {
 | 
				
			|||||||
    filename: String,
 | 
					    filename: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Download disk image
 | 
				
			||||||
 | 
					pub async fn download(p: web::Path<DiskFilePath>, req: HttpRequest) -> HttpResult {
 | 
				
			||||||
 | 
					    if !files_utils::check_file_name(&p.filename) {
 | 
				
			||||||
 | 
					        return Ok(HttpResponse::BadRequest().json("Invalid file name!"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let file_path = AppConfig::get()
 | 
				
			||||||
 | 
					        .disk_images_storage_path()
 | 
				
			||||||
 | 
					        .join(&p.filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if !file_path.exists() {
 | 
				
			||||||
 | 
					        return Ok(HttpResponse::NotFound().json("Disk image does not exists!"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(NamedFile::open(file_path)?.into_response(&req))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Delete a disk image
 | 
					/// Delete a disk image
 | 
				
			||||||
pub async fn delete(p: web::Path<DiskFilePath>) -> HttpResult {
 | 
					pub async fn delete(p: web::Path<DiskFilePath>) -> HttpResult {
 | 
				
			||||||
    if !files_utils::check_file_name(&p.filename) {
 | 
					    if !files_utils::check_file_name(&p.filename) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -345,6 +345,10 @@ async fn main() -> std::io::Result<()> {
 | 
				
			|||||||
                "/api/disk_images/list",
 | 
					                "/api/disk_images/list",
 | 
				
			||||||
                web::get().to(disk_images_controller::get_list),
 | 
					                web::get().to(disk_images_controller::get_list),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					            .route(
 | 
				
			||||||
 | 
					                "/api/disk_images/{filename}",
 | 
				
			||||||
 | 
					                web::get().to(disk_images_controller::download),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            .route(
 | 
					            .route(
 | 
				
			||||||
                "/api/disk_images/{filename}",
 | 
					                "/api/disk_images/{filename}",
 | 
				
			||||||
                web::delete().to(disk_images_controller::delete),
 | 
					                web::delete().to(disk_images_controller::delete),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,24 @@ export class DiskImageApi {
 | 
				
			|||||||
    ).data;
 | 
					    ).data;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Download disk image file
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  static async Download(
 | 
				
			||||||
 | 
					    file: DiskImage,
 | 
				
			||||||
 | 
					    progress: (p: number) => void
 | 
				
			||||||
 | 
					  ): Promise<Blob> {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      await APIClient.exec({
 | 
				
			||||||
 | 
					        method: "GET",
 | 
				
			||||||
 | 
					        uri: `/disk_images/${file.file_name}`,
 | 
				
			||||||
 | 
					        downProgress(e) {
 | 
				
			||||||
 | 
					          progress(Math.floor(100 * (e.progress / e.total)));
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    ).data;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Delete disk image file
 | 
					   * Delete disk image file
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,9 @@ import DeleteIcon from "@mui/icons-material/Delete";
 | 
				
			|||||||
import DownloadIcon from "@mui/icons-material/Download";
 | 
					import DownloadIcon from "@mui/icons-material/Download";
 | 
				
			||||||
import RefreshIcon from "@mui/icons-material/Refresh";
 | 
					import RefreshIcon from "@mui/icons-material/Refresh";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
 | 
					  Alert,
 | 
				
			||||||
  Button,
 | 
					  Button,
 | 
				
			||||||
 | 
					  CircularProgress,
 | 
				
			||||||
  IconButton,
 | 
					  IconButton,
 | 
				
			||||||
  LinearProgress,
 | 
					  LinearProgress,
 | 
				
			||||||
  Tooltip,
 | 
					  Tooltip,
 | 
				
			||||||
@@ -22,6 +24,7 @@ import { DateWidget } from "../widgets/DateWidget";
 | 
				
			|||||||
import { FileInput } from "../widgets/forms/FileInput";
 | 
					import { FileInput } from "../widgets/forms/FileInput";
 | 
				
			||||||
import { VirtWebPaper } from "../widgets/VirtWebPaper";
 | 
					import { VirtWebPaper } from "../widgets/VirtWebPaper";
 | 
				
			||||||
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
 | 
					import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
 | 
				
			||||||
 | 
					import { downloadBlob } from "../utils/FilesUtils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function DiskImagesRoute(): React.ReactElement {
 | 
					export function DiskImagesRoute(): React.ReactElement {
 | 
				
			||||||
  const [list, setList] = React.useState<DiskImage[] | undefined>();
 | 
					  const [list, setList] = React.useState<DiskImage[] | undefined>();
 | 
				
			||||||
@@ -159,6 +162,24 @@ function DiskImageList(p: {
 | 
				
			|||||||
  const confirm = useConfirm();
 | 
					  const confirm = useConfirm();
 | 
				
			||||||
  const loadingMessage = useLoadingMessage();
 | 
					  const loadingMessage = useLoadingMessage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [dlProgress, setDlProgress] = React.useState<undefined | number>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Download disk image file
 | 
				
			||||||
 | 
					  const downloadDiskImage = async (entry: DiskImage) => {
 | 
				
			||||||
 | 
					    setDlProgress(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const blob = await DiskImageApi.Download(entry, setDlProgress);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      downloadBlob(blob, entry.file_name);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      console.error(e);
 | 
				
			||||||
 | 
					      alert(`Failed to download disk image file! ${e}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setDlProgress(undefined);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Delete disk image
 | 
					  // Delete disk image
 | 
				
			||||||
  const deleteDiskImage = async (entry: DiskImage) => {
 | 
					  const deleteDiskImage = async (entry: DiskImage) => {
 | 
				
			||||||
    if (
 | 
					    if (
 | 
				
			||||||
@@ -220,7 +241,7 @@ function DiskImageList(p: {
 | 
				
			|||||||
        return (
 | 
					        return (
 | 
				
			||||||
          <>
 | 
					          <>
 | 
				
			||||||
            <Tooltip title="Download image">
 | 
					            <Tooltip title="Download image">
 | 
				
			||||||
              <IconButton onClick={() => downloadIso(params.row)}>
 | 
					              <IconButton onClick={() => downloadDiskImage(params.row)}>
 | 
				
			||||||
                <DownloadIcon />
 | 
					                <DownloadIcon />
 | 
				
			||||||
              </IconButton>
 | 
					              </IconButton>
 | 
				
			||||||
            </Tooltip>
 | 
					            </Tooltip>
 | 
				
			||||||
@@ -236,6 +257,31 @@ function DiskImageList(p: {
 | 
				
			|||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <DataGrid getRowId={(c) => c.file_name} rows={p.list} columns={columns} />
 | 
					    <>
 | 
				
			||||||
 | 
					      {/* Download notification */}
 | 
				
			||||||
 | 
					      {dlProgress !== undefined && (
 | 
				
			||||||
 | 
					        <Alert severity="info">
 | 
				
			||||||
 | 
					          <div
 | 
				
			||||||
 | 
					            style={{
 | 
				
			||||||
 | 
					              display: "flex",
 | 
				
			||||||
 | 
					              flexDirection: "row",
 | 
				
			||||||
 | 
					              alignItems: "center",
 | 
				
			||||||
 | 
					              overflow: "hidden",
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <Typography variant="body1">
 | 
				
			||||||
 | 
					              Downloading... {dlProgress}%
 | 
				
			||||||
 | 
					            </Typography>
 | 
				
			||||||
 | 
					            <CircularProgress
 | 
				
			||||||
 | 
					              variant="determinate"
 | 
				
			||||||
 | 
					              size={"1.5rem"}
 | 
				
			||||||
 | 
					              style={{ marginLeft: "10px" }}
 | 
				
			||||||
 | 
					              value={dlProgress}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </Alert>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					      <DataGrid getRowId={(c) => c.file_name} rows={p.list} columns={columns} />
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user