Skip to content

Commit 8a0a7a1

Browse files
committed
Add support for importing unzipped CSV
1 parent 8e10bf3 commit 8a0a7a1

File tree

2 files changed

+25
-17
lines changed

2 files changed

+25
-17
lines changed

frontend/my/src/Import.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class TheFormDef extends React.PureComponent {
118118
</Form.Item>
119119
<Form.Item
120120
{...formItemLayout}
121-
label="ZIP file">
121+
label="CSV or ZIP file">
122122
<div className="dropbox">
123123
{getFieldDecorator("file", {
124124
valuePropName: "file",
@@ -129,11 +129,11 @@ class TheFormDef extends React.PureComponent {
129129
multiple={ false }
130130
fileList={ this.state.fileList }
131131
beforeUpload={ this.onFileChange }
132-
accept=".zip">
132+
accept=".zip,.csv">
133133
<p className="ant-upload-drag-icon">
134134
<Icon type="inbox" />
135135
</p>
136-
<p className="ant-upload-text">Click or drag the ZIP file here</p>
136+
<p className="ant-upload-text">Click or drag a CSV or ZIP file here</p>
137137
</Upload.Dragger>
138138
)}
139139
</div>
@@ -316,8 +316,8 @@ class Import extends React.PureComponent {
316316
<hr />
317317
<div className="help">
318318
<h2>Instructions</h2>
319-
<p>Upload a ZIP file with a single CSV file in it
320-
to bulk import a large number of subscribers in a single shot.
319+
<p>Upload a CSV file or a ZIP file with a single CSV file in it
320+
to bulk import a subscribers.
321321
</p>
322322
<p>
323323
The CSV file should have the following headers with the exact column names

import.go

+20-12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"io/ioutil"
88
"net/http"
9+
"strings"
910

1011
"github.com/knadh/listmonk/subimporter"
1112
"github.com/labstack/echo"
@@ -37,8 +38,7 @@ func handleImportSubscribers(c echo.Context) error {
3738
}
3839

3940
if r.Mode != subimporter.ModeSubscribe && r.Mode != subimporter.ModeBlacklist {
40-
return echo.NewHTTPError(http.StatusBadRequest,
41-
"Invalid `mode`")
41+
return echo.NewHTTPError(http.StatusBadRequest, "Invalid `mode`")
4242
}
4343

4444
if len(r.Delim) != 1 {
@@ -78,17 +78,25 @@ func handleImportSubscribers(c echo.Context) error {
7878
}
7979
go impSess.Start()
8080

81-
// For now, we only extract 1 CSV from the ZIP. Handling async CSV
82-
// imports is more trouble than it's worth.
83-
dir, files, err := impSess.ExtractZIP(out.Name(), 1)
84-
if err != nil {
85-
return echo.NewHTTPError(http.StatusInternalServerError,
86-
fmt.Sprintf("Error extracting ZIP file: %v", err))
87-
} else if len(files) == 0 {
88-
return echo.NewHTTPError(http.StatusBadRequest,
89-
"No CSV files found to import.")
81+
if strings.HasSuffix(strings.ToLower(file.Filename), ".csv") {
82+
go impSess.LoadCSV(out.Name(), rune(r.Delim[0]))
83+
} else {
84+
// Only 1 CSV from the ZIP is considered. If multiple files have
85+
// to be processed, counting the net number of lines (to track progress),
86+
// keeping the global import state (failed / successful) etc. across
87+
// multiple files becomes complex. Instead, it's just easier for the
88+
// end user to concat multiple CSVs (if there are multiple in the first)
89+
// place and uploada as one in the first place.
90+
dir, files, err := impSess.ExtractZIP(out.Name(), 1)
91+
if err != nil {
92+
return echo.NewHTTPError(http.StatusInternalServerError,
93+
fmt.Sprintf("Error extracting ZIP file: %v", err))
94+
} else if len(files) == 0 {
95+
return echo.NewHTTPError(http.StatusBadRequest,
96+
"No CSV files found to import.")
97+
}
98+
go impSess.LoadCSV(dir+"/"+files[0], rune(r.Delim[0]))
9099
}
91-
go impSess.LoadCSV(dir+"/"+files[0], rune(r.Delim[0]))
92100

93101
return c.JSON(http.StatusOK, okResp{app.Importer.GetStats()})
94102
}

0 commit comments

Comments
 (0)