Skip to content

Commit

Permalink
fix: deadlock when download more than one item
Browse files Browse the repository at this point in the history
  • Loading branch information
XZB-1248 committed Jul 9, 2022
1 parent 2938019 commit 5558508
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 132 deletions.
24 changes: 17 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
## v0.1.3

* Optimize: basic operations for macOS.
* Fix: deadlock when download more than one item.

* 优化:macOS下,基础操作改为API调用的方式实现。
* 修复:下载目录或多文件时发生死锁,导致压缩文件不完整。



## v0.1.2

* Optimize: compress frontend assets.

* 优化: 压缩前端资源,加快加载速度。
* 优化压缩前端资源,加快加载速度。



Expand All @@ -15,10 +25,10 @@
* BREAKING-CHANGE: API `/device/file/get` parameter `file` changed to `files`.
* BREAKING-CHANGE: API `/device/file/remove` parameter `file` changed to `files`.

* 新增: 文本文件编辑器。
* 新增: 文件管理器多选。
* 新增: 文件管理器过滤。
* 修复: 一些潜在的bug。
* 新增文本文件编辑器。
* 新增文件管理器多选。
* 新增文件管理器过滤。
* 修复一些潜在的bug。
* 破坏性变动:API `/device/file/get` 参数 `file` 变为 `files`
* 破坏性变动:API `/device/file/remove` 参数 `file` 变为 `files`

Expand Down Expand Up @@ -49,8 +59,8 @@
* Add: file upload.
* Optimize: project structure.

* 新增: 文件上传功能。
* 优化: 项目结构。
* 新增文件上传功能。
* 优化项目结构。



Expand Down
101 changes: 44 additions & 57 deletions client/service/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@ func FetchFile(dir, file, bridge string) error {
return err
}

func getTempFileName(dir, file string) string {
exists := true
tempFile := ``
for i := 0; exists; i++ {
tempFile = path.Join(dir, file+`.tmp.`+strconv.Itoa(i))
_, err := os.Stat(tempFile)
if os.IsNotExist(err) {
exists = false
}
}
return tempFile
}

func RemoveFiles(files []string) error {
for i := 0; i < len(files); i++ {
if files[i] == `\` || files[i] == `/` || len(files[i]) == 0 {
Expand Down Expand Up @@ -273,6 +286,10 @@ func uploadMulti(files []string, writer *io.PipeWriter, req *req.Request) error
go func() {
for _, subJob := range spare {
lock.Lock()
if escape {
lock.Unlock()
break
}
queue <- subJob
lock.Unlock()
}
Expand All @@ -296,6 +313,10 @@ func uploadMulti(files []string, writer *io.PipeWriter, req *req.Request) error
return err
}
lock.Lock()
if escape {
lock.Unlock()
break
}
queue <- Job{stat, items[i], []string{stat.Name()}}
lock.Unlock()
}
Expand All @@ -312,31 +333,36 @@ func uploadMulti(files []string, writer *io.PipeWriter, req *req.Request) error
select {
case job := <-queue:
if escape {
// Try to get next job, to make locked producer unlock.
// Job is useless so there's no need to keep it.
lock.Lock()
if len(queue) > 0 {
_, _ = <-queue
}
lock.Unlock()
break
}
handleJob(job)
if escape {
// Try to get next job, to make locking producer unlock.
// Escaping now, job is useless so there's no need to keep it.
_, _ = <-queue
}
default:
escape = true
_, _ = <-queue
break
}
if escape {
lock.Lock()
close(queue)
lock.Unlock()
if len(fails) > 0 {
zipWriter.SetComment(`Those files could not be archived:` + "\n" + strings.Join(fails, "\n"))
if len(queue) > 0 {
_, _ = <-queue
}
zipWriter.Close()
writer.Close()
lock.Unlock()
break
}
}
if escape {
lock.Lock()
close(queue)
lock.Unlock()
if len(fails) > 0 {
zipWriter.SetComment(`Those files could not be archived:` + "\n" + strings.Join(fails, "\n"))
}
zipWriter.Close()
writer.Close()
}
}()
return nil
}
Expand All @@ -363,59 +389,20 @@ func UploadTextFile(path, bridge string) error {
})
uploadReq.RawRequest.ContentLength = size

// Check file if is a text file.
// UTF-8 and GBK are only supported yet.
// Check file if is a text file with UTF-8 encoding.
buf := make([]byte, size)
_, err = file.Read(buf)
if err != nil {
return err
}
if utf8.Valid(buf) {
uploadReq.SetHeader(`FileEncoding`, `UTF-8`)
} else if gbkValidate(buf) {
uploadReq.SetHeader(`FileEncoding`, `GBK`)
} else {
if !utf8.Valid(buf) {
return errors.New(`${i18n|fileEncodingUnsupported}`)
}

file.Seek(0, 0)
url := config.GetBaseURL(false) + `/api/bridge/push`
_, err = uploadReq.
SetBody(file).
SetBody(buf).
SetQueryParam(`bridge`, bridge).
Send(`PUT`, url)
return err
}

func gbkValidate(b []byte) bool {
length := len(b)
var i int = 0
for i < length {
if b[i] <= 0x7f {
i++
continue
} else {
if i+1 < length {
if b[i] >= 0x81 && b[i] <= 0xfe && b[i+1] >= 0x40 && b[i+1] <= 0xfe && b[i+1] != 0xf7 {
i += 2
continue
}
}
return false
}
}
return true
}

func getTempFileName(dir, file string) string {
exists := true
tempFile := ``
for i := 0; exists; i++ {
tempFile = path.Join(dir, file+`.tmp.`+strconv.Itoa(i))
_, err := os.Stat(tempFile)
if os.IsNotExist(err) {
exists = false
}
}
return tempFile
}
1 change: 1 addition & 0 deletions server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func main() {
gin.SetMode(gin.ReleaseMode)
}
app := gin.New()
app.Use(gin.Recovery())
if config.Config.Debug.Pprof {
pprof.Register(app)
}
Expand Down
86 changes: 38 additions & 48 deletions web/src/components/explorer.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
Space,
Spin
} from "antd";
import ProTable from "@ant-design/pro-table";
import {formatSize, orderCompare, post, preventClose, request, translate, waitTime} from "../utils/utils";
import ProTable, {TableDropdown} from "@ant-design/pro-table";
import {catchBlobReq, formatSize, orderCompare, post, preventClose, request, translate, waitTime} from "../utils/utils";
import dayjs from "dayjs";
import i18n from "../locale/locale";
import {VList} from "virtuallist-antd";
Expand Down Expand Up @@ -134,34 +134,40 @@ function FileBrowser(props) {
}, [props.device, props.visible]);

function renderOperation(file) {
const remove = (
<Popconfirm
key='remove'
title={
i18n.t('deleteConfirm').replace('{0}',
i18n.t(file.type === 0 ? 'file' : 'folder')
)
}
onConfirm={() => removeFiles(file.name)}
let menus = [
{key: 'delete', name: i18n.t('delete')},
{key: 'editAsText', name: i18n.t('editAsText')},
];
if (file.type === 1) {
menus.pop();
} else if (file.type === 2) {
return [];
}
if (file.name === '..') {
return [];
}
return [
<a
key='download'
onClick={() => downloadFiles(file.name)}
>
<a>{i18n.t('delete')}</a>
</Popconfirm>
);
switch (file.type) {
case 0:
return [
<a
key='download'
onClick={() => downloadFiles(file.name)}
>{i18n.t('download')}</a>,
remove,
];
case 1:
return [remove];
case 2:
return [];
{i18n.t('download')}
</a>,
<TableDropdown
key='more'
onSelect={key => onDropdownSelect(key, file)}
menus={menus}
/>,
];
}
function onDropdownSelect(key, file) {
switch (key) {
case 'delete':
removeFiles(file.name);
break;
case 'editAsText':
textEdit(file);
}
return [];
}
function onRowClick(file) {
const separator = props.isWindows ? '\\' : '/';
Expand Down Expand Up @@ -202,21 +208,13 @@ function FileBrowser(props) {
responseType: 'blob',
timeout: 10000
}).then(res => {
if (res.status !== 200) {
res.data.text().then((str) => {
let data = {};
try {
data = JSON.parse(str);
} catch (e) { }
message.warn(data.msg ? translate(data.msg) : i18n.t('requestFailed'));
});
} else {
if (res.status === 200) {
if (preview.length > 0) {
URL.revokeObjectURL(preview);
}
setPreview(URL.createObjectURL(res.data));
}
}).finally(() => {
}).catch(catchBlobReq).finally(() => {
setLoading(false);
});
}
Expand All @@ -229,22 +227,14 @@ function FileBrowser(props) {
responseType: 'blob',
timeout: 7000
}).then(res => {
if (res.status !== 200) {
res.data.text().then(str => {
let data = {};
try {
data = JSON.parse(str);
} catch (e) { }
message.warn(data.msg ? translate(data.msg) : i18n.t('requestFailed'));
});
} else {
if (res.status === 200) {
res.data.text().then(str => {
setEditingContent(str);
setDraggable(false);
setEditingFile(file.name);
});
}
}).finally(() => {
}).catch(catchBlobReq).finally(() => {
setLoading(false);
});
}
Expand Down
4 changes: 2 additions & 2 deletions web/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Overview from "./pages/overview";
import {translate} from "./utils/utils";

axios.defaults.baseURL = '.';
axios.interceptors.response.use(async (res) => {
axios.interceptors.response.use(async res => {
let data = res.data;
if (data.hasOwnProperty('code')) {
if (data.code !== 0){
Expand All @@ -26,7 +26,7 @@ axios.interceptors.response.use(async (res) => {
}
}
return Promise.resolve(res);
}, (err) => {
}, err => {
// console.error(err);
if (err.code === 'ECONNABORTED') {
message.error(i18n.t('requestTimeout'));
Expand Down
1 change: 1 addition & 0 deletions web/src/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"upload": "Upload",
"delete": "Delete",
"download": "Download",
"editAsText": "Edit as text",
"uploading": "Uploading...",
"uploadFailed": "Upload Failed",
"uploadAborted": "Upload Aborted",
Expand Down
1 change: 1 addition & 0 deletions web/src/locale/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"upload": "上传",
"delete": "删除",
"download": "下载",
"editAsText": "编辑文本",
"uploading": "上传中...",
"uploadFailed": "上传失败",
"uploadAborted": "取消上传",
Expand Down
Loading

0 comments on commit 5558508

Please sign in to comment.