整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

JavaScript 文件拖拽上传

件拖拽上传

使用HTML5的文件API, 可以将操作系统中的文件拖放到浏览器的指定区域, 实现文件上传到服务器。本文将结合实例讲解HTML5+jQuery+PHP实现拖拽上传图片的过程, 来看下HTML5的魅力吧。

HTML

我们在页面中放置一个拖拽区域#drop_area, 即接收拖拽的区域, #preview用来预览拖拽上传的图片信息。

<div id="drop_area">将图片拖拽到此区域</div>
<div id="preview"></div>

Javascript

要想实现拖拽, 页面需要阻止浏览器默认行为, 即四个事件(拖离、拖后放、拖进、拖来拖去), 因为我们要阻止浏览器默认将图片打开的行为, 这里我们使用jQuery来完成。

$(function(){
//阻止浏览器默认行。
$(document).on({
    dragleave:function(e){ //拖离
    e.preventDefault();
},
drop:function(e){ //拖后放
    e.preventDefault();
},
dragenter:function(e){ //拖进
    e.preventDefault();
},
dragover:function(e){ //拖来拖去
    e.preventDefault();
}
});
...
});

接下来我们来了解下文件API。HTML5的文件API有一个FileList接口, 它可以通过e.dataTransfer.files拖拽事件传递的文件信息, 获取本地文件列表信息

var fileList = e.dataTransfer.files;

在本例中, 我们用javascript来侦听drop事件, 首先要判断拖入的文件是否符合要求, 包括图片类型、大小等, 然后获取本地图片信息, 实现预览, 最后上传。

$(function(){
/// ...接上部分
var box = document.getElementById('drop_area'); //拖拽区域
box.addEventListener("drop",function(e){
e.preventDefault(); //取消默认浏览器拖拽效果
var fileList = e.dataTransfer.files; //获取文件对象
//检测是否是拖拽文件到页面的操作
if(fileList.length == 0){
    return false;
}
//检测文件是不是图片
if(fileList[0].type.indexOf('image') === -1){
alert("您拖的不是图片!");
return false;
}

//拖拉图片到浏览器,可以实现预览功能
var img = window.URL.createObjectURL(fileList[0]);
var filename = fileList[0].name; //图片名称
var filesize = Math.floor((fileList[0].size)/1024);
if(filesize>500){
alert("上传大小不能超过500K.");
return false;
}
var str = "<img src='"+img+"'><p>图片名称:"+filename+"</p><p>大小:"+filesize+"KB</p>";
$("#preview").html(str);

//上传
xhr = new XMLHttpRequest();
xhr.open("post", "upload.php", true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");

var fd = new FormData();
fd.append('mypic', fileList[0]);

xhr.send(fd);
},false);
});

我们用FormData模拟表单数据, 直接将数据append到formdata对象中, 实现了ajax上传。

PHP

upload.php用于接收上传的文件信息, 完成上传, 实现代码如下:

<?php
$mypic = $_FILES["mypic"];
if(!empty($mypic)){
$picname = $_FILES['mypic']['name'];
$picsize = $_FILES['mypic']['size'];
if ($picsize > 512000) {
echo '图片大小不能超过500k';
exit;
}
$type = strstr($picname, '.');
if ($type != ".gif" && $type != ".jpg") {
echo '图片格式不对!';
exit;
}
$pics = 'helloweba' . $type;
//上传路径
$pic_path = "pics/". $pics;
move_uploaded_file($mypic["tmp_name"],$pic_path);
}
?>

下边这几句可以没有

<meta charset="utf-8">
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="mypic">
<input type="submit" value="上传">
</form>

最后总结下HTML5实现拖拽上传的技术要点:

1、监听拖拽:监听页面元素的拖拽事件, 包括:dragenter、dragover、dragleave和drop, 一定要将dragover的默认事件取消掉, 不然无法触发drop事件。如需拖拽页面里的元素, 需要给其添加属性draggable=”true”;

2、获取拖拽文件:在drop事件触发后通过e.dataTransfer.files获取拖拽文件列表, .length属性获取文件数量, .type属性获取文件类型。

3、读取图片数据并添加预览图。

4、发送图片数据:使用FormData模拟表单数据AJAX提交文件流。

. pom.xml

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

2. application.properties

spring.servlet.multipart.max-request-size=10MB
spring.servlet.multipart.max-file-size=10MB

3. html

upload.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<h1>Spring Boot file upload example</h1>
<form method="POST" action="/upload" enctype="multipart/form-data">
 <input type="file" name="file" /><br/><br/>
 <input type="submit" value="提交" />
</form>
</body>
</html>

resut.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<div th:if="${message}">
 <h2 th:text="${message}"/>
</div>
</body>
</html>

4. SampleController

@Controller
public class SampleController {
 @GetMapping("/")
 public String upload() {
 return "upload";
 }
 @RequestMapping("/result")
 public String result() {
 return "result";
 }
 @PostMapping("/upload")
 public String singleFileUpload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) {
 if (file.isEmpty()) {
 redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
 return "redirect:result";
 }
 try {
 // Get the file and save it somewhere
 byte[] bytes = file.getBytes();
 Path path = Paths.get(uploadDirectory() + "/" + file.getOriginalFilename());
 Files.write(path, bytes);
 redirectAttributes.addFlashAttribute("message",
 file.getOriginalFilename() + " upload success");
 } catch (IOException e) {
 e.printStackTrace();
 }
 return "redirect:/result";
 }
 private String uploadDirectory() throws FileNotFoundException {
 //获取跟目录
 File path = new File(ResourceUtils.getURL("classpath:").getPath());
 if(!path.exists()) path = new File("");
 System.out.println("path:"+path.getAbsolutePath());
 //如果上传目录为/static/images/upload/,则可以如下获取:
 File upload = new File(path.getAbsolutePath(),"static/upload/");
 if(!upload.exists()) upload.mkdirs();
 System.out.println("upload url:"+upload.getAbsolutePath());
 //在开发测试模式时,得到的地址为:{项目跟目录}/target/static/images/upload/
 //在打包成jar正式发布时,得到的地址为:{发布jar包目录}/static/images/upload/
 return upload.getAbsolutePath();
 }
} 

5.访问 localhost:8080/

选择上传文件进行上传

本号主要用于分享企业中常用的技术,更加侧重于实用,欢迎关注,便于浏览其它更多实用的历史文章。

inIO

MinIO 是一款高性能、分布式的对象存储系统。它是一款软件产品, 可以100%的运行在标准硬件上。即X86等低成本机器也能够很好的运行MinIO。

MinIO与传统的存储和其他的对象存储不同的是:它一开始就针对性能要求更高的私有云标准进行软件架构设计。因为MinIO一开始就只为对象存储而设计。所以他采用了更易用的方式进行设计,它能实现对象存储所需要的全部功能,在性能上也更加强劲,它不会为了更多的业务功能而妥协,失去MinIO的易用性、高效性。 这样的结果所带来的好处是:它能够更简单的实现具有弹性伸缩能力的原生对象存储服务。

MinIO在传统对象存储用例(例如辅助存储,灾难恢复和归档)方面表现出色。同时,它在机器学习、大数据、私有云、混合云等方面的存储技术上也独树一帜。当然,也不排除数据分析、高性能应用负载、原生云的支持。

在中国:阿里巴巴、腾讯、百度、中国联通、华为、中国移动等等9000多家企业也都在使用MinIO产品。

安装

使用如下命令快速安装一个单机minio

# 下载 minio
wget https://dl.min.io/server/minio/release/linux-amd64/minio
# 添加可执行权限
chmod +x minio
# 设置登录minio的 access key
export MINIO_ACCESS_KEY=minioadmin
# 设置登录minio的 secret key
export MINIO_SECRET_KEY=minioadmin
# 启动 minio
./minio server /data

安装后使用浏览器访问http://IP地址:9000,如果可以访问,则表示minio已经安装成功。输入上面自定义的access key 和 secret key就可以登录了。

登录右下角加号创建mybucket桶

开放 mybucket 读写权限

创建项目操作 MinIO

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>io.minio</groupId>
	<artifactId>minio</artifactId>
	<version>7.0.1</version>
</dependency>
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>LATEST</version>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<optional>true</optional>
</dependency>
<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>2.6</version>
</dependency>

编辑配置文件application.yml修改MinIO相关配置


server:
  port: 8080

spring:
  application:
    name: minio-test

  thymeleaf:
    cache: false

  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 100MB

# minio 连接参数
minio:
  endpoint: 192.168.20.111
  port: 9000
  accessKey: minioadmin
  secretKey: minioadmin
  bucketName: mybucket

连接 MinIO 配置

@Data
@ConfigurationProperties(prefix = "minio")
@Component
public class MinioProp {
	private String endpoint;
	private String accesskey;
	private String secretKey;
	private int port;
}

创建 MinioClient

@Component
public class MinioConfiguration {

	@Autowired
	private MinioProp minioProp;

	@Bean
	public MinioClient minioClient() throws InvalidPortException, InvalidEndpointException {
		//MinioClient client = new MinioClient(minioProp.getEndpoint(), minioProp.getAccesskey(), minioProp.getSecretKey());
		MinioClient client  = MinioClient.builder().endpoint(minioProp.getEndpoint(), minioProp.getPort(), false).credentials(minioProp.getAccesskey(), minioProp.getSecretKey()).build();
		return client;
	}
}

MinIO 查看桶列表,存入,删除 操作 MinioController

@Slf4j
@RestController
public class MinioController {
    @Autowired
    private MinioClient minioClient;
 
    private static final String MINIO_BUCKET = "mybucket";
 
    @GetMapping("/list")
    public List<Object> list(ModelMap map) throws Exception {
        Iterable<Result<Item>> myObjects = minioClient.listObjects(MINIO_BUCKET);
        Iterator<Result<Item>> iterator = myObjects.iterator();
        List<Object> items = new ArrayList<>();
        String format = "{'fileName':'%s','fileSize':'%s'}";
        while (iterator.hasNext()) {
            Item item = iterator.next().get();
            items.add(JSON.parse(String.format(format, item.objectName(), formatFileSize(item.size()))));
        }
        return items;
    }
 
    @PostMapping("/upload")
    public Res upload(@RequestParam(name = "file", required = false) MultipartFile[] file) {
        Res res = new Res();
        res.setCode(500);
 
        if (file == null || file.length == 0) {
            res.setMessage("上传文件不能为空");
            return res;
        }
 
        List<String> orgfileNameList = new ArrayList<>(file.length);
 
        for (MultipartFile multipartFile : file) {
            String orgfileName = multipartFile.getOriginalFilename();
            orgfileNameList.add(orgfileName);
 
            try {
                InputStream in = multipartFile.getInputStream();
                minioClient.putObject(MINIO_BUCKET, orgfileName, in, new PutObjectOptions(in.available(), -1));
                in.close();
            } catch (Exception e) {
                log.error(e.getMessage());
                res.setMessage("上传失败");
                return res;
            }
        }
 
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("bucketName", MINIO_BUCKET);
        data.put("fileName", orgfileNameList);
        res.setCode(200);
        res.setMessage("上传成功");
        res.setData(data);
        return res;
    }
 
    @RequestMapping("/download/{fileName}")
    public void download(HttpServletResponse response, @PathVariable("fileName") String fileName) {
        InputStream in = null;
        try {
            ObjectStat stat = minioClient.statObject(MINIO_BUCKET, fileName);
            response.setContentType(stat.contentType());
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
 
            in = minioClient.getObject(MINIO_BUCKET, fileName);
            IOUtils.copy(in, response.getOutputStream());
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
        }
    }
 
    @DeleteMapping("/delete/{fileName}")
    public Res delete(@PathVariable("fileName") String fileName) {
        Res res = new Res();
        res.setCode(200);
        try {
            minioClient.removeObject(MINIO_BUCKET, fileName);
        } catch (Exception e) {
            res.setCode(500);
            log.error(e.getMessage());
        }
        return res;
    }
 
    private static String formatFileSize(long fileS) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        String wrongSize = "0B";
        if (fileS == 0) {
            return wrongSize;
        }
        if (fileS < 1024) {
            fileSizeString = df.format((double) fileS) + " B";
        } else if (fileS < 1048576) {
            fileSizeString = df.format((double) fileS / 1024) + " KB";
        } else if (fileS < 1073741824) {
            fileSizeString = df.format((double) fileS / 1048576) + " MB";
        } else {
            fileSizeString = df.format((double) fileS / 1073741824) + " GB";
        }
        return fileSizeString;
    }
}

Res返回数据封装


@lombok.Data
@AllArgsConstructor
@NoArgsConstructor
public class Res implements Serializable {
	private static final long serialVersionUID = 1L;
	private Integer code;
	private Object data = "";
	private String message = "";
}

路由文件 RouterController

@Controller
public class RouterController {
	@GetMapping({"/", "/index.html"})
	public String index() {
		return "index";
	}

	@GetMapping({"/upload.html"})
	public String upload() {
		return "upload";
	}
}

启动类

@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan(basePackages = "com.minio")
public class ServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ServerApplication.class, args);
	}

}


前端 列表页面 src\main\resources\templates\index.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8"/>
    <title>图片列表</title>
    <link rel="stylesheet" href="http://cdn.staticfile.org/element-ui/2.13.1/theme-chalk/index.css">
</head>
<body>
 
<div id="app">
 
    <el-link icon="el-icon-upload" href="/upload.html">上传图片</el-link>
    <br/>
 
    <el-table :data="results" stripe style="width: 60%" @row-click="preview">
        <el-table-column type="index" width="50"></el-table-column>
        <el-table-column prop="fileName" label="文件名" width="180"></el-table-column>
        <el-table-column prop="fileSize" label="文件大小"></el-table-column>
        <el-table-column label="操作">
            <template slot-scope="scope">
                <a :href="'/download/' + scope.row.fileName + ''" class="el-icon-download">下载</a>
                <a :href="'/delete/' + scope.row.fileName + ''" @click.prevent="deleteFile($event,scope.$index,results)"
                   class="el-icon-delete">删除</a>
            </template>
        </el-table-column>
    </el-table>
 
    <br/>
    <el-link icon="el-icon-picture">预览图片</el-link>
    <br/>
    <div class="demo-image__preview" v-if="previewImg">
        <el-image style="width: 100px; height: 100px" :src="imgSrc" :preview-src-list="imgList"></el-image>
    </div>
 
</div>
 
<script src="http://cdn.staticfile.org/vue/2.6.11/vue.min.js"></script>
<script src="http://cdn.staticfile.org/axios/0.19.2/axios.min.js"></script>
<script src="http://cdn.staticfile.org/element-ui/2.13.1/index.js"></script>
 
<script>
    new Vue({
        el: '#app',
        data: {
            bucketURL: 'http://192.168.20.111:9000/mybucket/',
            previewImg: true,
            results: [],
            imgSrc: '',
            imgList: []
        },
        methods: {
            init() {
                axios.get('/list').then(response => {
                    this.results = response.data;
                    if (this.results.length == 0) {
                        this.imgSrc = '';
                        this.previewImg = false;
                    } else {
                        for (var i = 0; i < this.results.length; i++) {
                            this.imgList.push(this.bucketURL + this.results[i].fileName);
                            if (i == 0) {
                                this.imgSrc = this.bucketURL + this.results[0].fileName;
                            }
                        }
                    }
                });
            },
            preview(row, event, column) {
                this.imgSrc = this.bucketURL + row.fileName;
                this.previewImg = true;
            },
            deleteFile(e,index,list) {
                axios.delete(e.target.href, {}).then(res => {
                    if (res.data.code == 200) {
                        this.$message('删除成功!');
                        list.splice(index, 1);
                        this.previewImg = false;
                    } else {
                        this.$message('删除失败!');
                    }
                });
            }
        },
        mounted() {
            this.init();
        }
    });
</script>
 
</body>
</html>

前端上传页面 src\main\resources\templates\upload.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>图片上传</title>
    <link rel="stylesheet" type="text/css" href="http://cdn.staticfile.org/webuploader/0.1.5/webuploader.css">
    <script type="text/javascript" src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
    <script type="text/javascript" src="http://cdn.staticfile.org/webuploader/0.1.5/webuploader.min.js"></script>
</head>
 
<body>
 
<div id="uploader-demo">
    <div id="fileList" class="uploader-list"></div>
    <div id="filePicker">选择图片</div>
</div>
<br/>
<a href="/index.html">返回图片列表页面</a>
 
<script type="text/javascript">
    var uploader = WebUploader.create({
        auto: true,
        swf: 'http://cdn.staticfile.org/webuploader/0.1.5/Uploader.swf',
        server: '/upload',
        pick: '#filePicker',
        accept: {
            title: 'Images',
            extensions: 'gif,jpg,jpeg,bmp,png',
            mimeTypes: 'image/*'
        }
    });
 
    uploader.on('fileQueued', function (file) {
        var $li = $(
            '<div id="' + file.id + '" class="file-item thumbnail">' +
            '<img>' +
            '<div class="info">' + file.name + '</div>' +
            '</div>'
            ),
            $img = $li.find('img');
 
        var $list = $("#fileList");
        $list.append($li);
 
        uploader.makeThumb(file, function (error, src) {
            if (error) {
                $img.replaceWith('<span>不能预览</span>');
                return;
            }
            $img.attr('src', src);
        }, 100, 100);
    });
 
    uploader.on('uploadProgress', function (file, percentage) {
        var $li = $('#' + file.id),
            $percent = $li.find('.progress span');
 
        if (!$percent.length) {
            $percent = $('<p class="progress"><span></span></p>')
                .appendTo($li)
                .find('span');
        }
        $percent.css('width', percentage * 100 + '%');
    });
 
    uploader.on('uploadSuccess', function (file) {
        $('#' + file.id).addClass('upload-state-done');
    });
 
    uploader.on('uploadError', function (file) {
        var $li = $('#' + file.id),
            $error = $li.find('div.error');
 
        if (!$error.length) {
            $error = $('<div class="error"></div>').appendTo($li);
        }
        $error.text('上传失败');
    });
 
    uploader.on('uploadComplete', function (file) {
        $('#' + file.id).find('.progress').remove();
    });
</script>
 
</body>
</html>

运行项目


列表页面

参考博客:

https://blog.csdn.net/jianzhang11/article/details/105672261