Skip to content

Commit 366871b

Browse files
committed
feat: export csv and excel
1 parent 6634e54 commit 366871b

File tree

5 files changed

+186
-78
lines changed

5 files changed

+186
-78
lines changed

backend/framework/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@
5252
<artifactId>bcpkix-jdk15on</artifactId>
5353
<version>1.68</version>
5454
</dependency>
55+
<dependency>
56+
<groupId>org.dhatim</groupId>
57+
<artifactId>fastexcel</artifactId>
58+
<version>0.18.4</version>
59+
</dependency>
5560
</dependencies>
5661

5762

backend/framework/src/main/java/org/jumpserver/chen/framework/console/dataview/DataView.java

+24-74
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,18 @@
44
import lombok.EqualsAndHashCode;
55
import org.jumpserver.chen.framework.console.action.DataViewAction;
66
import org.jumpserver.chen.framework.console.component.Logger;
7+
import org.jumpserver.chen.framework.console.dataview.export.DataExport;
78
import org.jumpserver.chen.framework.console.entity.response.SQLResult;
89
import org.jumpserver.chen.framework.console.state.DataViewState;
910
import org.jumpserver.chen.framework.console.state.StateManager;
10-
import org.jumpserver.chen.framework.datasource.entity.resource.Field;
1111
import org.jumpserver.chen.framework.datasource.sql.SQLQueryParams;
1212
import org.jumpserver.chen.framework.datasource.sql.SQLQueryResult;
1313
import org.jumpserver.chen.framework.jms.entity.CommandRecord;
1414
import org.jumpserver.chen.framework.session.SessionManager;
15-
import org.jumpserver.chen.framework.utils.CodeUtils;
1615
import org.jumpserver.chen.framework.ws.io.PacketIO;
1716

18-
import java.io.BufferedWriter;
19-
import java.io.IOException;
20-
import java.nio.file.Files;
21-
import java.sql.Clob;
17+
import java.io.File;
2218
import java.sql.SQLException;
23-
import java.text.SimpleDateFormat;
24-
import java.time.LocalDateTime;
25-
import java.time.format.DateTimeFormatter;
26-
import java.util.Date;
2719
import java.util.HashMap;
2820
import java.util.List;
2921
import java.util.Map;
@@ -74,7 +66,10 @@ public void doAction(DataViewAction action) throws SQLException {
7466
this.changeLimit((int) action.getData());
7567
}
7668
case DataViewAction.ACTION_EXPORT -> {
77-
this.export((String) action.getData());
69+
var data = (Map<String, String>) action.getData();
70+
var scope = data.get("scope");
71+
var format = data.get("format");
72+
this.export(scope, format);
7873
}
7974
}
8075
}
@@ -134,84 +129,39 @@ private void fullDataViewData(DataViewData viewData, SQLQueryResult result) {
134129
}
135130

136131

137-
private static void writeString(BufferedWriter writer, Object object) throws IOException {
138-
var str = object.toString();
139-
140-
if (str.contains(",")) {
141-
str = "\"" + str + "\"";
142-
}
143-
writer.write(str);
144-
}
145-
146-
private void writeCSVData(BufferedWriter writer, DataViewData viewData) throws IOException, SQLException {
147-
148-
for (Field field : viewData.getFields()) {
149-
writeString(writer, field.getName());
150-
writer.write(",");
151-
}
152-
for (Map<String, Object> row : viewData.getData()) {
153-
for (Field field : viewData.getFields()) {
154-
var obj = row.get(field.getName());
155-
if (obj == null) {
156-
writer.write("NULL");
157-
writer.write(",");
158-
} else if (obj instanceof Clob clob) {
159-
writer.write(CodeUtils.escapeCsvValue(clob.getSubString(1, (int) clob.length())));
160-
writer.write(",");
161-
} else if (obj instanceof Date) {
162-
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
163-
writeString(writer, fmt.format(obj));
164-
} else {
165-
writeString(writer, row.get(field.getName()));
166-
writer.write(",");
167-
}
168-
}
169-
writer.newLine();
170-
}
171-
172-
writer.newLine();
173-
}
174-
175-
public void export(String scope) throws SQLException {
132+
public void export(String scope, String format) throws SQLException {
176133
var session = SessionManager.getCurrentSession();
177134

178-
179-
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
180-
String timestamp = LocalDateTime.now().format(formatter);
181-
var f = session.createFile(String.format("data_%s.csv", timestamp));
182-
183135
CommandRecord command = new CommandRecord(String.format("Export data: %s", this.title));
184136

185137
try {
186138
if (!SessionManager.getCurrentSession().canDownload()) {
187-
session.getController().sendFile(f.getName());
188139
return;
189140
}
190-
var writer = Files.newBufferedWriter(f.toPath());
191-
192-
if (scope.equals("current")) {
193-
this.writeCSVData(writer, this.data);
194-
command.setOutput(String.format("%d rows exported", this.data.getData().size()));
195-
}
196-
197-
if (scope.equals("all")) {
198-
SQLQueryParams queryParams = new SQLQueryParams();
199-
queryParams.setLimit(-1);
200-
var result = this.loadDataInterface.loadData(queryParams);
201-
var viewData = new DataViewData();
202-
this.fullDataViewData(viewData, result);
203-
command.setOutput(String.format("%d rows exported", result.getData().size()));
141+
File f = null;
142+
switch (scope) {
143+
case "current":
144+
f = DataExport.export(format, this.data);
145+
command.setOutput(String.format("%d rows exported", this.data.getData().size()));
146+
break;
147+
case "all":
148+
SQLQueryParams queryParams = new SQLQueryParams();
149+
queryParams.setLimit(-1);
150+
var result = this.loadDataInterface.loadData(queryParams);
151+
var viewData = new DataViewData();
152+
this.fullDataViewData(viewData, result);
153+
f = DataExport.export(format, viewData);
154+
command.setOutput(String.format("%d rows exported", result.getData().size()));
155+
break;
204156
}
205-
writer.flush();
206-
writer.close();
207157

208158
this.consoleLogger.success(command.getOutput());
209159
session.recordCommand(command);
160+
session.getController().sendFile(f.getName());
210161

211-
} catch (IOException e) {
162+
} catch (Exception e) {
212163
throw new RuntimeException(e);
213164
}
214-
session.getController().sendFile(f.getName());
215165
}
216166

217167

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package org.jumpserver.chen.framework.console.dataview.export;
2+
3+
import org.dhatim.fastexcel.Workbook;
4+
import org.dhatim.fastexcel.Worksheet;
5+
import org.jumpserver.chen.framework.console.dataview.DataViewData;
6+
import org.jumpserver.chen.framework.datasource.entity.resource.Field;
7+
import org.jumpserver.chen.framework.session.SessionManager;
8+
import org.jumpserver.chen.framework.utils.CodeUtils;
9+
10+
import java.io.BufferedWriter;
11+
import java.io.File;
12+
import java.io.FileOutputStream;
13+
import java.io.IOException;
14+
import java.nio.file.Files;
15+
import java.nio.file.Path;
16+
import java.sql.Clob;
17+
import java.text.SimpleDateFormat;
18+
import java.time.LocalDateTime;
19+
import java.time.format.DateTimeFormatter;
20+
import java.util.Date;
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
25+
public class DataExport {
26+
public static File export(String format, DataViewData data) throws Exception {
27+
var session = SessionManager.getCurrentSession();
28+
29+
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
30+
String timestamp = LocalDateTime.now().format(formatter);
31+
32+
String filename = String.format("data_%s", timestamp);
33+
34+
File f;
35+
36+
switch (format) {
37+
case "excel":
38+
f = session.createFile(String.format("%s.xlsx", filename));
39+
new DataExportExcel().exportData(f.toPath().toString(), data);
40+
break;
41+
case "csv":
42+
f = session.createFile(String.format("%s.csv", filename));
43+
new DataExportCSV().exportData(f.toPath().toString(), data);
44+
break;
45+
default:
46+
throw new Exception("unsupported format: " + format);
47+
}
48+
return f;
49+
}
50+
}
51+
52+
53+
class DataExportExcel implements DataExportInterface {
54+
@Override
55+
public void exportData(String path, DataViewData data) throws Exception {
56+
try (FileOutputStream fos = new FileOutputStream(path);
57+
Workbook workbook = new Workbook(fos, "JumpServer", "4.0")) {
58+
59+
Worksheet sheet = workbook.newWorksheet("Data");
60+
List<Field> fields = data.getFields();
61+
List<Map<String, Object>> rows = data.getData();
62+
63+
for (int col = 0; col < fields.size(); col++) {
64+
sheet.value(0, col, fields.get(col).getName());
65+
}
66+
67+
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
68+
69+
for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
70+
Map<String, Object> row = rows.get(rowIndex);
71+
72+
for (int col = 0; col < fields.size(); col++) {
73+
Field field = fields.get(col);
74+
Object obj = row.get(field.getName());
75+
76+
if (obj == null) {
77+
sheet.value(rowIndex + 1, col, "NULL");
78+
} else if (obj instanceof Clob clob) {
79+
try {
80+
sheet.value(rowIndex + 1, col, clob.getSubString(1, (int) clob.length()));
81+
} catch (Exception e) {
82+
sheet.value(rowIndex + 1, col, "ERROR_CLOB");
83+
}
84+
} else if (obj instanceof Date) {
85+
sheet.value(rowIndex + 1, col, dateFormat.format(obj));
86+
} else {
87+
sheet.value(rowIndex + 1, col, obj.toString());
88+
}
89+
}
90+
}
91+
}
92+
}
93+
}
94+
95+
class DataExportCSV implements DataExportInterface {
96+
@Override
97+
public void exportData(String path, DataViewData data) throws Exception {
98+
var writer = Files.newBufferedWriter(Path.of(path));
99+
100+
for (Field field : data.getFields()) {
101+
writeString(writer, field.getName());
102+
writer.write(",");
103+
}
104+
for (Map<String, Object> row : data.getData()) {
105+
for (Field field : data.getFields()) {
106+
var obj = row.get(field.getName());
107+
if (obj == null) {
108+
writer.write("NULL");
109+
writer.write(",");
110+
} else if (obj instanceof Clob clob) {
111+
writer.write(CodeUtils.escapeCsvValue(clob.getSubString(1, (int) clob.length())));
112+
writer.write(",");
113+
} else if (obj instanceof Date) {
114+
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
115+
writeString(writer, fmt.format(obj));
116+
} else {
117+
writeString(writer, row.get(field.getName()));
118+
writer.write(",");
119+
}
120+
}
121+
writer.newLine();
122+
}
123+
124+
writer.newLine();
125+
writer.flush();
126+
writer.close();
127+
}
128+
129+
private static void writeString(BufferedWriter writer, Object object) throws IOException {
130+
var str = object.toString();
131+
132+
if (str.contains(",")) {
133+
str = "\"" + str + "\"";
134+
}
135+
writer.write(str);
136+
}
137+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.jumpserver.chen.framework.console.dataview.export;
2+
3+
import org.jumpserver.chen.framework.console.dataview.DataViewData;
4+
5+
public interface DataExportInterface {
6+
void exportData(String path, DataViewData data) throws Exception;
7+
}

frontend/src/components/Main/Explore/DataView/ExportDataDialog.vue

+13-4
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,16 @@
66
width="40%"
77
>
88
<el-form ref="form" :model="form" label-width="80px">
9-
<el-radio v-model="form.scope" label="current">{{ $tc('ExportCurrent') }}</el-radio>
10-
<el-radio v-model="form.scope" label="all">{{ $tc('ExportAll') }}</el-radio>
9+
10+
<el-form-item :label="$tc('Scope')">
11+
<el-radio v-model="form.scope" label="current">{{ $tc('ExportCurrent') }}</el-radio>
12+
<el-radio v-model="form.scope" label="all">{{ $tc('ExportAll') }}</el-radio>
13+
</el-form-item>
14+
15+
<el-form-item :label="$tc('Format')">
16+
<el-radio v-model="form.format" label="csv">CSV</el-radio>
17+
<el-radio v-model="form.format" label="excel">Excel</el-radio>
18+
</el-form-item>
1119
</el-form>
1220

1321
<span slot="footer" class="dialog-footer">
@@ -35,7 +43,8 @@ export default {
3543
data() {
3644
return {
3745
form: {
38-
scope: 'current'
46+
scope: 'current',
47+
format: 'csv'
3948
}
4049
}
4150
},
@@ -53,7 +62,7 @@ export default {
5362
},
5463
methods: {
5564
onSubmit() {
56-
this.$emit('submit', this.form.scope)
65+
this.$emit('submit', { scope: this.form.scope, format: this.form.format })
5766
}
5867
}
5968
}

0 commit comments

Comments
 (0)