微信小程序上传图片组件,图片实现拖拽排序
-
微信小程序上封装上传图片组件
-
安装 weapp
-
components
-
components/imgUp/index.wxml
<view class="img-box"> <HFilesImg fileList="{{fileList}}" show-upload="{{showUpload}}" multiple="{{multiple}}" bind:updateImageList="updateImageList" maxCount="{{maxCount}}" previewSize="{{previewSize}}" deletable="{{deletable}}" bind:onAfterRead="onAfterRead" bind:onDelete="onDelete"></HFilesImg> </view>
-
components/imgUp/index.json
{ "component": true, "usingComponents": { "van-cell": "@vant/weapp/cell/index", "HFilesImg": "../h-files-img/index" } }
-
components/imgUp/index.ts
import { http } from "../../utils/api"; Component({ /** * 组件的属性列表 */ properties: { value: { type: Array, value: [], }, type: { type: String, value: "", }, Id: { type: String, value: "", }, position: { type: String, value: "", }, multiple: { type: Boolean, value: true, }, deletable: { type: Boolean, value: true, }, showUpload: { type: Boolean, value: true, }, maxCount: { type: Number, value: 9, }, uploadUrl: { type: String, value: "修改图片url地址", }, deleteUrl: { type: String, value: "删除图片url地址", }, gap: { type: Number, value: 9 }, columns: { type: Number, value: 3 }, // // 列数 previewSize: { type: Number, value: 100 }, // 图片大小 deleteStyle: { type: String, value: "" }, }, /** * 组件的初始数据 */ data: { fileList: [], }, lifetimes: { attached: function () { }, }, /** * 组件的方法列表 */ methods: { updateImageList(e: any) { console.log(e.detail); if (e.detail.length === 0) { return; } else { const list = e.detail.map((el: any, index: number) => ({ id: el.id, order: index + 1, })); http({ url: "NewHousing/MoveChange", method: "POST", data: { movechangelist: list, }, }).then((res: any) => { if (res.errCode === 0) { console.log(res); } }); } }, // 文件读取完成 onAfterRead(e: any) { e.detail.file.map((s: any) => { if (s.size > 4 * 1024 * 1024) { wx.showModal({ content: "请上传小于4M的图片", showCancel: false, success: () => {}, }); } else { this.data.fileList.push({ url: s.url, name: "图片" + this.data.fileList.length, state: 0, // 0:上传中 1:上传成功 2:上传失败 fileId: null, }); } }); this.setData({ fileList: this.data.fileList, }); this.onAddFiles(); }, /** 附件上传 */ onAddFiles() { let formDataObj: any = {}; if (this.properties.Id) { formDataObj.id = this.properties.Id; } for (let index = 0; index < this.data.fileList.length; index++) { const el:any = this.data.fileList[index]; wx.uploadFile({ url: this.properties.Id ? this.properties.uploadUrl : '新增url地址', filePath: el.url, name: "file", //上传的所需字段,后端提供 header: { Authorization: 'token值', }, formData: formDataObj, success: (res: any) => { const data = JSON.parse(res.data); const arr: any[] = this.data.fileList.concat([]); arr[index]["id"] = data.data.id; arr[index]["url"] = data.data.filepath; arr[index]["state"] = 1; this.setData({ fileList: arr }); this.triggerEvent("onChange", this.data.fileList); }, fail: () => { const arr: any[] = this.data.fileList.concat([]); arr[index]["state"] = 2; this.setData({ fileList: arr }); this.triggerEvent("onChange", this.data.fileList); }, }); } }, // 点击删除文件 onDelete(e: any) { if (this.properties.Id) { const that = this; wx.showModal({ title: "提示", content: "确定要删除图片吗?", success(res) { if (res.confirm) { console.log("用户点击确定"); const Id: any = that.properties.Id; const list: any = { id: Number(Id), attachid: e.detail.file.id, // 附件id }; if (that.properties.type || that.properties.type === "0") { list.type = that.properties.type; } that.getDelete(list,that.properties.deleteUrl, e.detail.index); } }, }); } else { this.data.fileList.splice(e.detail.index, 1); this.setData({ fileList: this.data.fileList, }); this.triggerEvent("onChange", this.data.fileList); } }, getDelete(list: any, url: any, index: any) { wx.showLoading({ title: "loading...", }); http({ url: url, method: "POST", data: list, }) .then((res: any) => { if (res.errCode === 0) { wx.showToast({ title: "删除成功", icon: "success", }); this.data.fileList.splice(index, 1); this.setData({ fileList: this.data.fileList, }); this.triggerEvent("onChange", this.data.fileList); } }) .catch((err: any) => { const data = err.data; wx.showModal({ title: "提示", showCancel: false, content: `${data.Message}`, }); }) .then(() => { wx.hideLoading(); }); }, }, observers: { value: function (value) { this.setData({ fileList: value || [], }); }, }, });
-
components/imgUp/index.scss
.img-box { padding: 0 13px 13px; background-color: #fff; }
- h-files-img 拖拽图片上传组件
-
components/h-files-img/index.wxml
<view style="width: {{containerRes.width}}px; height: {{containerRes.height}}px;" class="drag-container"> <view wx:for="{{dragImgList}}" wx:key="index" catch:tap="openImg" data-url="{{item.url}}" style="transform: translate({{index === currentIndex ? tranX : item.tranX}}px, {{index === currentIndex ? tranY : item.tranY}}px); z-index: {{index === currentIndex ? 10 : 1}}; width: {{previewSize}}px; height: {{previewSize}}px;" class="drag-item drag-item-transition" mark:index="{{index}}" mark:key="{{item.key}}" catch:longpress="longPress" catch:touchmove="touchMove" catch:touchend="touchEnd"> <image class="drag-item-img" src="{{item.url}}" /> <view wx:if="{{deletable}}" catch:tap="deleteImg" mark:key="{{index}}" data-item="{{item}}" class="drag-item-delete"> <view class="drag-item-delete_default" style="{{deleteStyle}}">x</view> </view> </view> <view wx:if="{{showUpload}}"> <view catch:tap="uploadImage" class="drag-item drag-upload" hidden="{{dragImgList.length >= maxCount}}" style="transform: translate({{uploadPosition.tranX}}px, {{uploadPosition.tranY}}px); width: {{previewSize}}px; height: {{previewSize}}px;"> <view class="drag-upload_solt"> <slot name="upload"></slot> </view> <view class="drag-upload_default"> <van-icon name="plus" color="#dcdee0" size="24px" /> </view> </view> </view> </view>
-
components/h-files-img/index.json
{ "component": true, "usingComponents": { "van-uploader": "@vant/weapp/uploader/index", "van-icon": "@vant/weapp/icon/index" } }
-
components/h-files-img/index.ts
Component({ options: { multipleSlots: !0 }, properties: { showUpload: { type: Boolean, value: false, }, previewSize: { type: Number, value: 100 }, // 图片大小 fileList: { // 初始化图片数组 type: Array, value: [], observer(t) { if (t?.length) { const e = this.getDragImgList(t); this.setUploaPosition(e.length), this.setData({ dragImgList: e }) } else { this.setData({ dragImgList: [], uploadPosition: { tranX: 0, tranY: 0, } }) } } }, deletable: { type: Boolean, value: false, }, maxCount: { type: Number, value: 9 },// 图片上传数量限制 columns: { type: Number, value: 3 }, // // 列数 gap: { type: Number, value: 15 }, // // 图片间隔 deleteStyle: { type: String, value: "" } // 删除样式 }, data: { dragImgList: [], containerRes: { top: 0, // 容器距离页面顶部距离 px left: 0, // 容器距离页面左边距离 px width: 100, // 容器宽度 px height: 100, // 容器高度 px }, // 拖拽容器属性 currentKey: -1, // 正在拖拽图片的key currentIndex: -1, // 正在拖拽图片的index tranX: 0, // 正在拖拽图片移动的x距离 tranY: 0, // 正在拖拽图片移动的y距离 uploadPosition: { // upload上传图标位移距离 tranX: 0, tranY: 0, }, type: '' }, lifetimes: { ready() { this.createSelectorQuery().select(".drag-container").boundingClientRect((({ top: t, left: e }) => { this.setData({ "containerRes.top": t, "containerRes.left": e }) })).exec() } }, methods: { openImg(e: any) { const array = this.data.dragImgList.map((el:any) => el.url) wx.previewImage({ current: e.currentTarget.dataset.url, // 当前显示图片的http链接 urls: array // 需要预览的图片http链接列表 }) }, /** * 长按图片 */ longPress(e: any) { const index = e.mark.index; const { pageX, pageY } = e.touches[0]; const { previewSize, containerRes: { top, left } } = this.data; this.setData({ currentIndex: index, tranX: pageX - previewSize / 2 - left, tranY: pageY - previewSize / 2 - top, type: 'move' }); }, /** * touchMove */ touchMove(e: any) { // 如果currentIndex < 0,说明并没有触发longPress if (this.data.currentIndex < 0) { return; } const { pageX, pageY } = e.touches[0]; const { previewSize, containerRes: { top, left } } = this.data; const tranX = pageX - previewSize / 2 - left; const tranY = pageY - previewSize / 2 - top; this.setData({ tranX, tranY, type: 'move' }); // 对比当前移动的key和停放位置的key,如果不一样就修改位置 const currentKey = e.mark.key; const moveKey = this.getMoveKey(tranX, tranY); // 当移动的key和正在停放位置的key相等,就无须处理 if (currentKey === moveKey || this.data.currentKey === currentKey) { return; } this.data.currentKey = currentKey; this.replace(currentKey, moveKey); }, /** * 计算移动中的key * @param tranX 正在拖拽图片的tranX * @param tranY 正在拖拽图片的tranY */ getMoveKey(tranX: number, tranY: number) { const { dragImgList: list, previewSize, columns } = this.data; const _getPositionNumber = (drag: any, limit: number) => { const positionNumber = Math.round(drag / previewSize); return positionNumber >= limit ? limit - 1 : positionNumber < 0 ? 0 : positionNumber; } const endKey = columns * _getPositionNumber(tranY, Math.ceil(list.length / columns)) + _getPositionNumber(tranX, columns); return endKey >= list.length ? list.length - 1 : endKey; }, /** * 生成拖拽后的新数组 * @param start 拖拽起始的key * @param end 拖拽结束的key */ replace(start: number, end: number) { const dragImgList = this.data.dragImgList; dragImgList.forEach((item: any) => { if (start < end) { if (item.key > start && item.key <= end) item.key--; else if (item.key === start) item.key = end; } else if (start > end) { if (item.key >= end && item.key < start) item.key++; else if (item.key === start) item.key = end; } }); this.getListPosition(dragImgList); }, /** * 计算数组的位移位置 * @param list 拖拽图片数组 */ getListPosition(list: any) { const { previewSize, columns, gap } = this.data; const dragImgList = list.map((item: any) => { item.tranX = (previewSize + gap) * (item.key % columns); item.tranY = Math.floor(item.key / columns) * (previewSize + gap); return item; }) this.setData({ dragImgList, }); this.updateEvent(dragImgList); }, /** * touchEnd */ touchEnd() { this.setData({ tranX: 0, tranY: 0, currentIndex: -1, type: 'move' }); this.data.currentKey = -1; }, /** * updateEvent * @describe 上传删除拖拽后触发事件把列表数据发给页面 */ updateEvent(dragImgList: any) { const list = [...dragImgList].sort((a, b) => a.key - b.key).map((item) => ({ url: item.url, id: item.id })); this.triggerEvent('updateImageList', list); }, /** * 上传图片 */ async uploadImage() { let { dragImgList, maxCount } = this.data; try { const res = await wx.chooseMedia({ count: maxCount - dragImgList.length, mediaType: ['image'], }); // 获取上传图片数据后需要初始化图片拽结构 const imgList = this.getDragImgList(res?.tempFiles?.map(({ tempFilePath, size }) => ({ tempFilePath, size })) || [], false); dragImgList = dragImgList.concat(imgList); // 修改上传区域位置 this.setUploaPosition(dragImgList.length); this.setData({ dragImgList, type: '' }); const list = { file: imgList } this.triggerEvent("onAfterRead", list); } catch (error) { console.log(error); } }, /** * 改变图片数量后获取容器宽高 * @parma listLength 数组长度 */ getContainerRect(listLength: number) { const { columns, previewSize, maxCount, gap } = this.data; const number = listLength === maxCount ? listLength : listLength + 1; const row = Math.ceil(number / columns) return { width: columns * previewSize + (columns - 1) * gap, height: row * previewSize + gap * (row - 1), }; }, /** * 根据图片列表生成拖拽列表数据结构 * @param list 图片url列表 * @param init 是否是初始化 */ getDragImgList(list: any, init = true) { let { dragImgList, previewSize, columns, gap } = this.data; return list.map((item: any, index: number) => { const i = (init ? 0 : dragImgList.length) + index; return { tranX: (previewSize + gap) * (i % columns), tranY: Math.floor(i / columns) * (previewSize + gap), url: item.tempFilePath ? item.tempFilePath : item.url, size: item.size || '', id: item.id ? item.id : null, key: i, }; }); }, /** * 修改上传区域位置 * @param listLength 数组长度 */ setUploaPosition(listLength: number) { const { previewSize, columns, gap } = this.data; const uploadPosition = { tranX: listLength % columns * (previewSize + gap), tranY: Math.floor(listLength / columns) * (previewSize + gap), }; const { width, height } = this.getContainerRect(listLength); this.setData({ uploadPosition, ['containerRes.width']: width, ['containerRes.height']: height, }); }, /** * 删除图片 */ deleteImg(e: any) { const key = e.mark.key; const obj = { file: e.currentTarget.dataset.item, index: key } this.triggerEvent("onDelete", obj); }, } });
-
components/h-files-img/index.scss
.drag-container { position: relative; } .drag-item { position: absolute; top: 0; left: 0; } .drag-item-transition { transition: transform 0.1s } .drag-item-img { width: 100%; height: 100%; } .drag-item-delete { position: absolute; top: 0; right: 0; touch-action: none; } .drag-item-delete_default { display: flex; width: 21px; height: 15px; line-height: 10px; justify-content: center; background-color: rgba(0, 0, 0, 0.7); border-radius: 0 0 0 12px; color: #FEFEFE; } .drag-upload .drag-upload_solt:empty+.drag-upload_default { width: 100%; height: 100%; box-sizing: border-box; text-align: center; font-weight: bold; line-height: 100%; background-color: #f7f8fa; } .drag-upload .drag-upload_default { display: flex; align-items: center; justify-content: center; }
- h-files-img 纯图片上传
-
components/h-files-img/index.wxml
<view style="max-width:{{width}}px;"> <van-uploader file-list="{{ fileList }}" preview-size="{{previewSize}}" multiple="{{multiple}}" upload-icon="plus" show-upload="{{showUpload}}" deletable="{{deletable}}" max-count="{{maxCount}}" bind:delete="onDelete" bind:after-read="afterRead" /> </view>
-
components/h-files-img/index.json
{ "component": true, "usingComponents": { "van-uploader": "@vant/weapp/uploader/index" } }
-
components/h-files-img/index.ts
Component({ /** * 组件的属性列表 */ properties: { name: { type: String, value: "img", }, fileList: { type: Array, value: [], }, previewSize: { type: Number, value: 80, }, multiple: { type: Boolean, value: true, }, showUpload: { type: Boolean, value: false, }, deletable: { type: Boolean, value: false, }, maxCount: { type: Number, value: 9, }, columnNum: { type: Number, value: 4, }, }, /** * 组件的初始数据 */ data: { width: 352, }, lifetimes: { attached() { this.setData({ width: (this.data.previewSize + 8) * this.data.columnNum, }); }, }, /** * 组件的方法列表 */ methods: { // 文件读取完成后 afterRead(event) { this.triggerEvent("onAfterRead", event.detail); }, // 点击删除文件 onDelete(e) { this.triggerEvent("onDelete", e.detail); }, }, });
-
pages
-
pages/index/index.wxml
<imgUp maxCount="999" Id="{{id}}" value="{{imgList}}" />
-
pages/index/index.json
{ "navigationBarTitleText": "拖拽图片", "usingComponents": { "imgUp": "/components/imgUp/index" } }
-
pages/index/index.ts
data: { imgList: [] }
-
utils/api.ts
import { getToken } from "./util"; const baseUrl = "xx/api/"; // 后端接口 export default baseUrl; export function http({ url = "", data = {}, method = "GET", header = {} }: IHttpOption = {}) { return new Promise(async (resolve: any, reject: any) => { const token = await getToken(); hrefUrl = getBaseUrl(url) wx.request({ url: hrefUrl, data: data, header: { Authorization: token ? `${token.type} ${token.token}` : "", ...header, }, method: method, complete: (res: any) => { if (res.statusCode >= 200 && res.statusCode < 300) { resolve(res.data); } else { const statusCode = res.statusCode; if (statusCode === 401) { wx.clearStorage(); wx.showModal({ title: "提示", content: "登录失效请重新登录", showCancel: false, success: function (res) { if (res.confirm) { wx.nextTick(() => { wx.navigateTo({ url: "/pages/login/index", }); }); } }, }); } else if (statusCode === 404) { wx.showModal({ title: "提示", content:"接口未发布请联系管理员", showCancel: false, }); } else { reject(res); } } }, }); }); } export function httpVisitors({ url = "", data = {}, method = "GET", header = {} }: IHttpOption = {}) { return new Promise(async (resolve: any, reject: any) => { wx.request({ url: getBaseUrl(url), data: data, header: { ...header, }, method: method, complete: (res: any) => { if (res.statusCode >= 200 && res.statusCode < 300) { resolve(res.data); } else { reject(res); } }, }); }); } export function getBaseUrl(url: any) { if (url.indexOf("://") == -1) { url = baseUrl + url; } return url; } // get方法 export function _get(url: any, data = {}) { return http({ url, data, }); } export function _post(url: any, data = {}) { return http({ url, data, method: "POST", }); }
-
上传FormData格式
import { httpVisitors } from "./api"; const FormData = require('./formData.js') const formData = new FormData(); formData.append('xxx', xxx); let data = formData.getData(); httpVisitors({ url: "", method: "POST", header: { 'content-type': data.contentType, }, data: data.buffer, }) .then((res: any) => {})
-
utils/util.ts
const FormData = require('./formData.js') /** 获取用户token */ export async function getToken(username = '', password = '') { let token: any = _token; if (!token) { const tokenStr = wx.getStorageSync("token"); if (tokenStr) { token = JSON.parse(tokenStr); } } if (token && Number(new Date()) < token.expires) { _token = token; return token; } else { wx.removeStorageSync("token"); } const formData = new FormData(); const name = username || wx.getStorageSync("loginName"); const pass = password || wx.getStorageSync("loginPass"); formData.append('username', name); formData.append('password', pass); formData.append('code', 2); let data = formData.getData(); return await httpVisitors({ url: "", method: "POST", header: { 'content-type': data.contentType, }, data: data.buffer, }) .then((res: any) => { wx.hideLoading(); if (res.errCode === -1) { wx.showModal({ title: '提示', content: res.errMsg, showCancel: false, }); return '' } else if (res.errCode === 0) { const data = res.data; const tokenJson = { type: 'bearer', token: data, expires: Number(new Date()) + 7200 * 1000, }; _token = tokenJson; const tokenJsonStr = JSON.stringify(tokenJson); wx.setStorageSync("token", tokenJsonStr); return tokenJson; } }) .catch(err => { const { statusCode } = err; if (statusCode === 404) { wx.hideLoading(); wx.showModal({ title: '提示', content: "接口未发布请联系管理员", showCancel: false, }); } else { wx.hideLoading(); return err; } }); }
-
utils/formData.js
const mimeMap = require('./mimeMap.js') function FormData(){ let fileManager = wx.getFileSystemManager(); let data = {}; let files = []; this.append = (name, value)=>{ data[name] = value; return true; } this.appendFile = (name, path, fileName)=>{ let buffer = fileManager.readFileSync(path); if(Object.prototype.toString.call(buffer).indexOf("ArrayBuffer") < 0){ return false; } if(!fileName){ fileName = getFileNameFromPath(path); } files.push({ name: name, buffer: buffer, fileName: fileName }); return true; } this.getData = ()=>convert(data, files) } function getFileNameFromPath(path){ let idx=path.lastIndexOf("/"); return path.substr(idx+1); } function convert(data, files){ let boundaryKey = 'wxmpFormBoundary' + randString(); // 数据分割符,一般是随机的字符串 let boundary = '--' + boundaryKey; let endBoundary = boundary + '--'; let postArray = []; //拼接参数 if(data && Object.prototype.toString.call(data) == "[object Object]"){ for(let key in data){ postArray = postArray.concat(formDataArray(boundary, key, data[key])); } } //拼接文件 if(files && Object.prototype.toString.call(files) == "[object Array]"){ for(let i in files){ let file = files[i]; postArray = postArray.concat(formDataArray(boundary, file.name, file.buffer, file.fileName)); } } //结尾 let endBoundaryArray = []; endBoundaryArray.push(...endBoundary.toUtf8Bytes()); postArray = postArray.concat(endBoundaryArray); return { contentType: 'multipart/form-data; boundary=' + boundaryKey, buffer: new Uint8Array(postArray).buffer } } function randString() { let res = ""; for (let i = 0; i < 17; i++) { let n = parseInt(Math.random() * 62); if (n <= 9) { res += n; } else if (n <= 35) { res += String.fromCharCode(n + 55); } else { res += String.fromCharCode(n + 61); } } return res; } function formDataArray(boundary, name, value, fileName){ let dataString = ''; let isFile = !!fileName; dataString += boundary + '\r\n'; dataString += 'Content-Disposition: form-data; name="' + name + '"'; if (isFile){ dataString += '; filename="' + fileName + '"' + '\r\n'; dataString += 'Content-Type: ' + getFileMime(fileName) + '\r\n\r\n'; } else{ dataString += '\r\n\r\n'; dataString += value; } var dataArray = []; dataArray.push(...dataString.toUtf8Bytes()); if (isFile) { let fileArray = new Uint8Array(value); dataArray = dataArray.concat(Array.prototype.slice.call(fileArray)); } dataArray.push(..."\r".toUtf8Bytes()); dataArray.push(..."\n".toUtf8Bytes()); return dataArray; } function getFileMime(fileName){ let idx = fileName.lastIndexOf("."); let mime = mimeMap[fileName.substr(idx)]; return mime?mime:"application/octet-stream" } String.prototype.toUtf8Bytes = function(){ var str = this; var bytes = []; for (var i = 0; i < str.length; i++) { bytes.push(...str.utf8CodeAt(i)); if (str.codePointAt(i) > 0xffff) { i++; } } return bytes; } String.prototype.utf8CodeAt = function(i) { var str = this; var out = [], p = 0; var c = str.charCodeAt(i); if (c < 128) { out[p++] = c; } else if (c < 2048) { out[p++] = (c >> 6) | 192; out[p++] = (c & 63) | 128; } else if ( ((c & 0xFC00) == 0xD800) && (i + 1) < str.length && ((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) { // Surrogate Pair c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF); out[p++] = (c >> 18) | 240; out[p++] = ((c >> 12) & 63) | 128; out[p++] = ((c >> 6) & 63) | 128; out[p++] = (c & 63) | 128; } else { out[p++] = (c >> 12) | 224; out[p++] = ((c >> 6) & 63) | 128; out[p++] = (c & 63) | 128; } return out; }; module.exports = FormData;
-
utils/mimeMap.js
module.exports = { "0.001": "application/x-001", "0.323": "text/h323", "0.907": "drawing/907", ".acp": "audio/x-mei-aac", ".aif": "audio/aiff", ".aiff": "audio/aiff", ".asa": "text/asa", ".asp": "text/asp", ".au": "audio/basic", ".awf": "application/vnd.adobe.workflow", ".bmp": "application/x-bmp", ".c4t": "application/x-c4t", ".cal": "application/x-cals", ".cdf": "application/x-netcdf", ".cel": "application/x-cel", ".cg4": "application/x-g4", ".cit": "application/x-cit", ".cml": "text/xml", ".cmx": "application/x-cmx", ".crl": "application/pkix-crl", ".csi": "application/x-csi", ".cut": "application/x-cut", ".dbm": "application/x-dbm", ".dcd": "text/xml", ".der": "application/x-x509-ca-cert", ".dib": "application/x-dib", ".doc": "application/msword", ".drw": "application/x-drw", ".dwf": "Model/vnd.dwf", ".dwg": "application/x-dwg", ".dxf": "application/x-dxf", ".emf": "application/x-emf", ".ent": "text/xml", ".eps": "application/x-ps", ".etd": "application/x-ebx", ".fax": "image/fax", ".fif": "application/fractals", ".frm": "application/x-frm", ".gbr": "application/x-gbr", ".gif": "image/gif", ".gp4": "application/x-gp4", ".hmr": "application/x-hmr", ".hpl": "application/x-hpl", ".hrf": "application/x-hrf", ".htc": "text/x-component", ".html": "text/html", ".htx": "text/html", ".ico": "image/x-icon", ".iff": "application/x-iff", ".igs": "application/x-igs", ".img": "application/x-img", ".isp": "application/x-internet-signup", ".java": "java/*", ".jpe": "image/jpeg", ".jpeg": "image/jpeg", ".jpg": "application/x-jpg", ".jsp": "text/html", ".lar": "application/x-laplayer-reg", ".lavs": "audio/x-liquid-secure", ".lmsff": "audio/x-la-lms", ".ltr": "application/x-ltr", ".m2v": "video/x-mpeg", ".m4e": "video/mpeg4", ".man": "application/x-troff-man", ".mdb": "application/msaccess", ".mfp": "application/x-shockwave-flash", ".mhtml": "message/rfc822", ".mid": "audio/mid", ".mil": "application/x-mil", ".mnd": "audio/x-musicnet-download", ".mocha": "application/x-javascript", ".mp1": "audio/mp1", ".mp2v": "video/mpeg", ".mp4": "video/mpeg4", ".mpd": "application/vnd.ms-project", ".mpeg": "video/mpg", ".mpga": "audio/rn-mpeg", ".mps": "video/x-mpeg", ".mpv": "video/mpg", ".mpw": "application/vnd.ms-project", ".mtx": "text/xml", ".net": "image/pnetvue", ".nws": "message/rfc822", ".out": "application/x-out", ".p12": "application/x-pkcs12", ".p7c": "application/pkcs7-mime", ".p7r": "application/x-pkcs7-certreqresp", ".pc5": "application/x-pc5", ".pcl": "application/x-pcl", ".pdf": "application/pdf", ".pdx": "application/vnd.adobe.pdx", ".pgl": "application/x-pgl", ".pko": "application/vnd.ms-pki.pko", ".plg": "text/html", ".plt": "application/x-plt", ".png": "application/x-png", ".ppa": "application/vnd.ms-powerpoint", ".pps": "application/vnd.ms-powerpoint", ".ppt": "application/x-ppt", ".prf": "application/pics-rules", ".prt": "application/x-prt", ".ps": "application/postscript", ".pwz": "application/vnd.ms-powerpoint", ".ra": "audio/vnd.rn-realaudio", ".ras": "application/x-ras", ".rdf": "text/xml", ".red": "application/x-red", ".rjs": "application/vnd.rn-realsystem-rjs", ".rlc": "application/x-rlc", ".rm": "application/vnd.rn-realmedia", ".rmi": "audio/mid", ".rmm": "audio/x-pn-realaudio", ".rms": "application/vnd.rn-realmedia-secure", ".rmx": "application/vnd.rn-realsystem-rmx", ".rp": "image/vnd.rn-realpix", ".rsml": "application/vnd.rn-rsml", ".rtf": "application/msword", ".rv": "video/vnd.rn-realvideo", ".sat": "application/x-sat", ".sdw": "application/x-sdw", ".slb": "application/x-slb", ".slk": "drawing/x-slk", ".smil": "application/smil", ".snd": "audio/basic", ".sor": "text/plain", ".spl": "application/futuresplash", ".ssm": "application/streamingmedia", ".stl": "application/vnd.ms-pki.stl", ".sty": "application/x-sty", ".swf": "application/x-shockwave-flash", ".tg4": "application/x-tg4", ".tif": "image/tiff", ".tiff": "image/tiff", ".top": "drawing/x-top", ".tsd": "text/xml", ".uin": "application/x-icq", ".vcf": "text/x-vcard", ".vdx": "application/vnd.visio", ".vpg": "application/x-vpeg005", ".vsd": "application/x-vsd", ".vst": "application/vnd.visio", ".vsw": "application/vnd.visio", ".vtx": "application/vnd.visio", ".wav": "audio/wav", ".wb1": "application/x-wb1", ".wb3": "application/x-wb3", ".wiz": "application/msword", ".wk4": "application/x-wk4", ".wks": "application/x-wks", ".wma": "audio/x-ms-wma", ".wmf": "application/x-wmf", ".wmv": "video/x-ms-wmv", ".wmz": "application/x-ms-wmz", ".wpd": "application/x-wpd", ".wpl": "application/vnd.ms-wpl", ".wr1": "application/x-wr1", ".wrk": "application/x-wrk", ".ws2": "application/x-ws", ".wsdl": "text/xml", ".xdp": "application/vnd.adobe.xdp", ".xfd": "application/vnd.adobe.xfd", ".xhtml": "text/html", ".xls": "application/x-xls", ".xml": "text/xml", ".xq": "text/xml", ".xquery": "text/xml", ".xsl": "text/xml", ".xwd": "application/x-xwd", ".sis": "application/vnd.symbian.install", ".x_t": "application/x-x_t", ".apk": "application/vnd.android.package-archive", "0.301": "application/x-301", "0.906": "application/x-906", ".a11": "application/x-a11", ".ai": "application/postscript", ".aifc": "audio/aiff", ".anv": "application/x-anv", ".asf": "video/x-ms-asf", ".asx": "video/x-ms-asf", ".avi": "video/avi", ".biz": "text/xml", ".bot": "application/x-bot", ".c90": "application/x-c90", ".cat": "application/vnd.ms-pki.seccat", ".cdr": "application/x-cdr", ".cer": "application/x-x509-ca-cert", ".cgm": "application/x-cgm", ".class": "java/*", ".cmp": "application/x-cmp", ".cot": "application/x-cot", ".crt": "application/x-x509-ca-cert", ".css": "text/css", ".dbf": "application/x-dbf", ".dbx": "application/x-dbx", ".dcx": "application/x-dcx", ".dgn": "application/x-dgn", ".dll": "application/x-msdownload", ".dot": "application/msword", ".dtd": "text/xml", ".dwf": "application/x-dwf", ".dxb": "application/x-dxb", ".edn": "application/vnd.adobe.edn", ".eml": "message/rfc822", ".epi": "application/x-epi", ".eps": "application/postscript", ".exe": "application/x-msdownload", ".fdf": "application/vnd.fdf", ".fo": "text/xml", ".g4": "application/x-g4", ".tif": "image/tiff", ".gl2": "application/x-gl2", ".hgl": "application/x-hgl", ".hpg": "application/x-hpgl", ".hqx": "application/mac-binhex40", ".hta": "application/hta", ".htm": "text/html", ".htt": "text/webviewhtml", ".icb": "application/x-icb", ".ico": "application/x-ico", ".ig4": "application/x-g4", ".iii": "application/x-iphone", ".ins": "application/x-internet-signup", ".IVF": "video/x-ivf", ".jfif": "image/jpeg", ".jpe": "application/x-jpe", ".jpg": "image/jpeg", ".js": "application/x-javascript", ".la1": "audio/x-liquid-file", ".latex": "application/x-latex", ".lbm": "application/x-lbm", ".ls": "application/x-javascript", ".m1v": "video/x-mpeg", ".m3u": "audio/mpegurl", ".mac": "application/x-mac", ".math": "text/xml", ".mdb": "application/x-mdb", ".mht": "message/rfc822", ".mi": "application/x-mi", ".midi": "audio/mid", ".mml": "text/xml", ".mns": "audio/x-musicnet-stream", ".movie": "video/x-sgi-movie", ".mp2": "audio/mp2", ".mp3": "audio/mp3", ".mpa": "video/x-mpg", ".mpe": "video/x-mpeg", ".mpg": "video/mpg", ".mpp": "application/vnd.ms-project", ".mpt": "application/vnd.ms-project", ".mpv2": "video/mpeg", ".mpx": "application/vnd.ms-project", ".mxp": "application/x-mmxp", ".nrf": "application/x-nrf", ".odc": "text/x-ms-odc", ".p10": "application/pkcs10", ".p7b": "application/x-pkcs7-certificates", ".p7m": "application/pkcs7-mime", ".p7s": "application/pkcs7-signature", ".pci": "application/x-pci", ".pcx": "application/x-pcx", ".pdf": "application/pdf", ".pfx": "application/x-pkcs12", ".pic": "application/x-pic", ".pl": "application/x-perl", ".pls": "audio/scpls", ".png": "image/png", ".pot": "application/vnd.ms-powerpoint", ".ppm": "application/x-ppm", ".ppt": "application/vnd.ms-powerpoint", ".pr": "application/x-pr", ".prn": "application/x-prn", ".ps": "application/x-ps", ".ptn": "application/x-ptn", ".r3t": "text/vnd.rn-realtext3d", ".ram": "audio/x-pn-realaudio", ".rat": "application/rat-file", ".rec": "application/vnd.rn-recording", ".rgb": "application/x-rgb", ".rjt": "application/vnd.rn-realsystem-rjt", ".rle": "application/x-rle", ".rmf": "application/vnd.adobe.rmf", ".rmj": "application/vnd.rn-realsystem-rmj", ".rmp": "application/vnd.rn-rn_music_package", ".rmvb": "application/vnd.rn-realmedia-vbr", ".rnx": "application/vnd.rn-realplayer", ".rpm": "audio/x-pn-realaudio-plugin", ".rt": "text/vnd.rn-realtext", ".rtf": "application/x-rtf", ".sam": "application/x-sam", ".sdp": "application/sdp", ".sit": "application/x-stuffit", ".sld": "application/x-sld", ".smi": "application/smil", ".smk": "application/x-smk", ".sol": "text/plain", ".spc": "application/x-pkcs7-certificates", ".spp": "text/xml", ".sst": "application/vnd.ms-pki.certstore", ".stm": "text/html", ".svg": "text/xml", ".tdf": "application/x-tdf", ".tga": "application/x-tga", ".tif": "application/x-tif", ".tld": "text/xml", ".torrent": "application/x-bittorrent", ".txt": "text/plain", ".uls": "text/iuls", ".vda": "application/x-vda", ".vml": "text/xml", ".vsd": "application/vnd.visio", ".vss": "application/vnd.visio", ".vst": "application/x-vst", ".vsx": "application/vnd.visio", ".vxml": "text/xml", ".wax": "audio/x-ms-wax", ".wb2": "application/x-wb2", ".wbmp": "image/vnd.wap.wbmp", ".wk3": "application/x-wk3", ".wkq": "application/x-wkq", ".wm": "video/x-ms-wm", ".wmd": "application/x-ms-wmd", ".wml": "text/vnd.wap.wml", ".wmx": "video/x-ms-wmx", ".wp6": "application/x-wp6", ".wpg": "application/x-wpg", ".wq1": "application/x-wq1", ".wri": "application/x-wri", ".ws": "application/x-ws", ".wsc": "text/scriptlet", ".wvx": "video/x-ms-wvx", ".xdr": "text/xml", ".xfdf": "application/vnd.adobe.xfdf", ".xls": "application/vnd.ms-excel", ".xlw": "application/x-xlw", ".xpl": "audio/scpls", ".xql": "text/xml", ".xsd": "text/xml", ".xslt": "text/xml", ".x_b": "application/x-x_b", ".sisx": "application/vnd.symbian.install", ".ipa": "application/vnd.iphone", ".xap": "application/x-silverlight-app", ".zip": "application/x-zip-compressed", }