Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.uci.ics.texera.web.service

import java.io.{ByteArrayInputStream, ByteArrayOutputStream}
import java.nio.charset.StandardCharsets
import java.util
import java.util.concurrent.{Executors, ThreadPoolExecutor}
import com.github.tototoshi.csv.CSVWriter
Expand Down Expand Up @@ -74,8 +75,8 @@ class ResultExportService(opResultStorage: OpResultStorage, wId: UInteger) {
handleGoogleSheetRequest(cache, request, results, attributeNames)
case "csv" =>
handleCSVRequest(user, request, results, attributeNames)
case "binary" =>
handleBinaryRequest(user, request, results)
case "data" =>
handleDataRequest(user, request, results)
case _ =>
ResultExportResponse("error", s"Unknown export type: ${request.exportType}")
}
Expand Down Expand Up @@ -176,7 +177,7 @@ class ResultExportService(opResultStorage: OpResultStorage, wId: UInteger) {
targetSheet.getSpreadsheetId
}

private def handleBinaryRequest(
private def handleDataRequest(
user: User,
request: ResultExportRequest,
results: Iterable[Tuple]
Expand All @@ -191,47 +192,32 @@ class ResultExportService(opResultStorage: OpResultStorage, wId: UInteger) {
}

val selectedRow = results.toSeq(rowIndex)

val field: Any = selectedRow.getField(columnIndex)

// Ensure the field is of type byte[]
val binaryData: Array[Byte] = field match {
// Convert the field to a byte array, regardless of its type
val dataBytes: Array[Byte] = field match {
case data: Array[Byte] => data
case data: AnyRef
if data.getClass.isArray && data.getClass.getComponentType == classOf[Byte] =>
data.asInstanceOf[Array[Byte]]
case _ =>
return ResultExportResponse(
"error",
s"Expected binary data (Array[Byte]), but got: ${field.getClass}"
)
case data: String => data.getBytes(StandardCharsets.UTF_8)
case data => data.toString.getBytes(StandardCharsets.UTF_8)
}

// Save the binary file (similar to how files are saved in the CSV handler)
binaryData match {
case data: Array[Byte] =>
val byteArray = data
val fileStream = new ByteArrayInputStream(byteArray)

// Save the binary file
request.datasetIds.foreach { did =>
val datasetPath = PathUtils.getDatasetPath(UInteger.valueOf(did))
val filePath = datasetPath.resolve(filename)
createNewDatasetVersionByAddingFiles(
UInteger.valueOf(did),
user,
Map(filePath -> fileStream)
)
}

ResultExportResponse(
"success",
s"Binary file $filename saved to Datasets ${request.datasetIds.mkString(",")}"
)
// Save the data file
val fileStream = new ByteArrayInputStream(dataBytes)

case _ =>
ResultExportResponse("error", s"Selected field is not binary data")
request.datasetIds.foreach { did =>
val datasetPath = PathUtils.getDatasetPath(UInteger.valueOf(did))
val filePath = datasetPath.resolve(filename)
createNewDatasetVersionByAddingFiles(
UInteger.valueOf(did),
user,
Map(filePath -> fileStream)
)
}

ResultExportResponse(
"success",
s"Data file $filename saved to Datasets ${request.datasetIds.mkString(",")}"
)
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="centered-container">
<div
*ngIf="exportType === 'binary'"
*ngIf="exportType === 'data'"
class="input-wrapper">
<nz-form-item>
<nz-form-label nzFor="filenameInput">Filename</nz-form-label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,25 +98,25 @@ <h5 class="rightAlign"><span [innerHTML]="compare(column.header, 'other')"></spa
</tr>
</thead>
<tbody>
<tr *ngFor="let row of basicTable.data; let i = index">
<tr
*ngFor="let row of basicTable.data; let i = index"
class="table-row-hover">
<td
(click)="open(i, row)"
*ngFor="let column of currentColumns; let columnIndex = index"
class="table-row"
class="table-cell"
nzEllipsis
ngClass="data-size">
(click)="open(i, row)">
<span class="cell-content">{{ column.getCell(row) }}</span>
<button
*ngIf="isBinaryData(currentResult[i][column.columnDef])"
(click)="downloadBinaryData(currentResult[i][column.columnDef], i, columnIndex, column.columnDef); $event.stopPropagation()"
(click)="downloadData(currentResult[i][column.columnDef], i, columnIndex, column.columnDef); $event.stopPropagation()"
nz-button
nz-dropdown
class="borderless-button"
title="export binary data">
nzType="link"
class="download-button"
title="Download data">
<i
nz-icon
nzType="cloud-download"></i>
</button>
{{ column.getCell(row) }}
</td>
</tr>
</tbody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,43 @@ th.header-size {
background-color: rgba(0, 0, 0, 0.05);
}
}

.table-cell {
position: relative;
padding-right: 28px;
}

.cell-content {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.download-button {
position: absolute;
right: 4px;
top: 50%;
transform: translateY(-50%);
opacity: 0;
transition: opacity 0.2s ease-in-out;
padding: 4px;
background: transparent;
border: none;
cursor: pointer;

i {
font-size: 16px; // Slightly larger to match the cloud icon
color: #1890ff; // Ant Design's primary blue color
}
}

.table-row-hover:hover {
.download-button {
opacity: 0.7;

&:hover {
opacity: 1;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -358,21 +358,14 @@ export class ResultTableFrameComponent implements OnInit, OnChanges {
return trimAndFormatData(cellContent, TABLE_COLUMN_TEXT_LIMIT);
}

isBinaryData(cellContent: any): boolean {
if (typeof cellContent === "string") {
return isBase64(cellContent) || isBinary(cellContent);
}
return false;
}

downloadBinaryData(binaryData: any, rowIndex: number, columnIndex: number, columnName: string): void {
downloadData(data: any, rowIndex: number, columnIndex: number, columnName: string): void {
const realRowNumber = (this.currentPageIndex - 1) * this.pageSize + rowIndex;
const defaultFileName = `${columnName}_${realRowNumber}`;
const modal = this.modalService.create({
nzTitle: "Export Binary Data and Save to a Dataset",
nzTitle: "Export Data and Save to a Dataset",
nzContent: ResultExportationComponent,
nzData: {
exportType: "binary",
exportType: "data",
workflowName: this.workflowActionService.getWorkflowMetadata.name,
defaultFileName: defaultFileName,
rowIndex: realRowNumber,
Expand Down